From 357429b8ea232619cdbdeda44e5f4b94c7a40f26 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 8 Jan 2021 14:48:08 +0100 Subject: [PATCH 001/402] Add .nfo ratings tag --- .../Parsers/BaseNfoParser.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index c287113c5..2a64bb243 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -624,6 +624,21 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } + case "ratings": + { + if (!reader.IsEmptyElement) + { + using var subtree = reader.ReadSubtree(); + FetchFromRatingsNode(subtree, item); + } + else + { + reader.Read(); + } + + break; + } + case "aired": case "formed": case "premiered": @@ -866,6 +881,90 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } + private void FetchFromRatingsNode(XmlReader reader, T item) + { + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "rating": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + + var ratingName = reader.GetAttribute("name"); + + using var subtree = reader.ReadSubtree(); + FetchFromRatingNode(subtree, item, ratingName); + + break; + } + + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); + } + } + } + + private void FetchFromRatingNode(XmlReader reader, T item, string? ratingName) + { + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "value": + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (float.TryParse(val.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) + { + // if ratingName contains tomato --> assume critic rating + if (ratingName != null && ratingName.Contains("tomato", StringComparison.InvariantCultureIgnoreCase)) + { + item.CriticRating = ratingValue; + } + else + { + item.CommunityRating = ratingValue; + } + } + } + + break; + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); + } + } + } + /// /// Gets the persons from XML node. /// From 620fbf0f89d33ab5d4d66373c7f9fe4580691f22 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 9 Jan 2021 10:51:59 +0100 Subject: [PATCH 002/402] Remove CropWhitespace function --- Emby.Drawing/ImageProcessor.cs | 5 -- Jellyfin.Api/Controllers/ImageController.cs | 5 +- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 85 ++----------------- .../Drawing/ImageProcessingOptions.cs | 3 - 4 files changed, 6 insertions(+), 92 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 8a2301d2d..af6e5b339 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -181,11 +181,6 @@ namespace Emby.Drawing { if (!File.Exists(cacheFilePath)) { - if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath)) - { - options.CropWhiteSpace = false; - } - string resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat); if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase)) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index c606d327c..cd1296310 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1681,7 +1681,7 @@ namespace Jellyfin.Api.Controllers int? width, int? height, int? quality, - bool? cropWhitespace, + bool? cropWhitespace, // TODO: Remove bool? addPlayedIndicator, int? blur, string? backgroundColor, @@ -1756,7 +1756,6 @@ namespace Jellyfin.Api.Controllers backgroundColor, foregroundLayer, imageInfo, - cropWhitespace.Value, outputFormats, cacheDuration, responseHeaders, @@ -1855,7 +1854,6 @@ namespace Jellyfin.Api.Controllers string? backgroundColor, string? foregroundLayer, ItemImageInfo imageInfo, - bool cropWhitespace, IReadOnlyCollection supportedFormats, TimeSpan? cacheDuration, IDictionary headers, @@ -1868,7 +1866,6 @@ namespace Jellyfin.Api.Controllers var options = new ImageProcessingOptions { - CropWhiteSpace = cropWhitespace, Height = height, ImageIndex = index ?? 0, Image = imageInfo, diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index eab5777d5..a786c01a6 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -91,9 +91,6 @@ namespace Jellyfin.Drawing.Skia } } - private static bool IsTransparent(SKColor color) - => (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0; - /// /// Convert a to a . /// @@ -111,65 +108,6 @@ namespace Jellyfin.Drawing.Skia }; } - private static bool IsTransparentRow(SKBitmap bmp, int row) - { - for (var i = 0; i < bmp.Width; ++i) - { - if (!IsTransparent(bmp.GetPixel(i, row))) - { - return false; - } - } - - return true; - } - - private static bool IsTransparentColumn(SKBitmap bmp, int col) - { - for (var i = 0; i < bmp.Height; ++i) - { - if (!IsTransparent(bmp.GetPixel(col, i))) - { - return false; - } - } - - return true; - } - - private SKBitmap CropWhiteSpace(SKBitmap bitmap) - { - var topmost = 0; - while (topmost < bitmap.Height && IsTransparentRow(bitmap, topmost)) - { - topmost++; - } - - int bottommost = bitmap.Height; - while (bottommost >= 0 && IsTransparentRow(bitmap, bottommost - 1)) - { - bottommost--; - } - - var leftmost = 0; - while (leftmost < bitmap.Width && IsTransparentColumn(bitmap, leftmost)) - { - leftmost++; - } - - var rightmost = bitmap.Width; - while (rightmost >= 0 && IsTransparentColumn(bitmap, rightmost - 1)) - { - rightmost--; - } - - var newRect = SKRectI.Create(leftmost, topmost, rightmost - leftmost, bottommost - topmost); - - using var image = SKImage.FromBitmap(bitmap); - using var subset = image.Subset(newRect); - return SKBitmap.FromImage(subset); - } - /// /// The path is null. /// The path is not valid. @@ -312,22 +250,11 @@ namespace Jellyfin.Drawing.Skia return resultBitmap; } - private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) - { - if (cropWhitespace) - { - using var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin); - return bitmap == null ? null : CropWhiteSpace(bitmap); - } - - return Decode(path, forceAnalyzeBitmap, orientation, out origin); - } - - private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation) + private SKBitmap? GetBitmap(string path, bool autoOrient, ImageOrientation? orientation) { if (autoOrient) { - var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out var origin); + var bitmap = Decode(path, true, orientation, out var origin); if (bitmap != null && origin != SKEncodedOrigin.TopLeft) { @@ -340,7 +267,7 @@ namespace Jellyfin.Drawing.Skia return bitmap; } - return GetBitmap(path, cropWhitespace, false, orientation, out _); + return Decode(path, false, orientation, out _); } private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) @@ -466,7 +393,7 @@ namespace Jellyfin.Drawing.Skia var blur = options.Blur ?? 0; var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0); - using var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation); + using var bitmap = GetBitmap(inputPath, autoOrient, orientation); if (bitmap == null) { throw new InvalidDataException($"Skia unable to read image {inputPath}"); @@ -474,9 +401,7 @@ namespace Jellyfin.Drawing.Skia var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height); - if (!options.CropWhiteSpace - && options.HasDefaultOptions(inputPath, originalImageSize) - && !autoOrient) + if (options.HasDefaultOptions(inputPath, originalImageSize) && !autoOrient) { // Just spit out the original file if all the options are default return inputPath; diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 22105b7d7..22de9a43e 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -24,8 +24,6 @@ namespace MediaBrowser.Controller.Drawing public int ImageIndex { get; set; } - public bool CropWhiteSpace { get; set; } - public int? Width { get; set; } public int? Height { get; set; } @@ -106,7 +104,6 @@ namespace MediaBrowser.Controller.Drawing PercentPlayed.Equals(0) && !UnplayedCount.HasValue && !Blur.HasValue && - !CropWhiteSpace && string.IsNullOrEmpty(BackgroundColor) && string.IsNullOrEmpty(ForegroundLayer); } From 262c6ae249b91d3fb986994a369cb83aafeb1ce8 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 9 Jan 2021 15:33:39 +0100 Subject: [PATCH 003/402] Remove ',' hack --- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 2a64bb243..2954868fc 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -938,10 +938,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - if (float.TryParse(val.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) + if (float.TryParse(val, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) { // if ratingName contains tomato --> assume critic rating - if (ratingName != null && ratingName.Contains("tomato", StringComparison.InvariantCultureIgnoreCase)) + if (ratingName != null && ratingName.Contains("tomato", StringComparison.OrdinalIgnoreCase)) { item.CriticRating = ratingValue; } From cf9a03790b4b5c77411b03a73df112f060c0ed02 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 20 Jan 2021 20:32:45 +0100 Subject: [PATCH 004/402] Check rating name for "audience" --- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 2954868fc..2e1fa4375 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -941,7 +941,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (float.TryParse(val, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var ratingValue)) { // if ratingName contains tomato --> assume critic rating - if (ratingName != null && ratingName.Contains("tomato", StringComparison.OrdinalIgnoreCase)) + if (ratingName != null && + ratingName.Contains("tomato", StringComparison.OrdinalIgnoreCase) && + !ratingName.Contains("audience", StringComparison.OrdinalIgnoreCase)) { item.CriticRating = ratingValue; } From a5e55ba85956fe54539cb22dfd95c9499aa3de6c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Feb 2021 15:59:27 -0500 Subject: [PATCH 005/402] Clean up UserManager.AuthenticateUser --- .../Users/UserManager.cs | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d1de5408c..38a074e98 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -402,27 +402,18 @@ namespace Jellyfin.Server.Implementations.Users } var user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); - bool success; - IAuthenticationProvider? authenticationProvider; + var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint) + .ConfigureAwait(false); + var authenticationProvider = authResult.authenticationProvider; + var success = authResult.success; - if (user != null) + if (user is null) { - var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint) - .ConfigureAwait(false); - authenticationProvider = authResult.authenticationProvider; - success = authResult.success; - } - else - { - var authResult = await AuthenticateLocalUser(username, password, null, remoteEndPoint) - .ConfigureAwait(false); - authenticationProvider = authResult.authenticationProvider; string updatedUsername = authResult.username; - success = authResult.success; if (success - && authenticationProvider != null - && !(authenticationProvider is DefaultAuthenticationProvider)) + && authenticationProvider is not null + && authenticationProvider is not DefaultAuthenticationProvider) { // Trust the username returned by the authentication provider username = updatedUsername; From 3a9fcb6abd249759bd44b2fbcf437beee8f611c2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 12 Feb 2021 17:34:51 +0100 Subject: [PATCH 006/402] Rewrite packet writing code for HdHomerun --- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 243 +++++------------- MediaBrowser.Common/Crc32.cs | 89 +++++++ tests/Jellyfin.Common.Tests/Crc32Tests.cs | 23 ++ .../LiveTv/HdHomerunManagerTests.cs | 39 +++ 4 files changed, 211 insertions(+), 183 deletions(-) create mode 100644 MediaBrowser.Common/Crc32.cs create mode 100644 tests/Jellyfin.Common.Tests/Crc32Tests.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index f09338330..188eca158 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -2,6 +2,7 @@ using System; using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; using System.Globalization; using System.Net; @@ -10,6 +11,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common; using MediaBrowser.Controller.LiveTv; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun @@ -120,13 +122,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private static async Task CheckTunerAvailability(NetworkStream stream, int tuner, CancellationToken cancellationToken) { - var lockkeyMsg = CreateGetMessage(tuner, "lockkey"); - await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false); - byte[] buffer = ArrayPool.Shared.Rent(8192); try { - int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + var msgLen = WriteGetMessage(buffer, tuner, "lockkey"); + await stream.WriteAsync(buffer.AsMemory(0, msgLen), cancellationToken).ConfigureAwait(false); + + int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); ParseReturnMessage(buffer, receivedBytes, out string returnVal); @@ -166,9 +168,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _activeTuner = i; var lockKeyString = string.Format(CultureInfo.InvariantCulture, "{0:d}", lockKeyValue); - var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null); - await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false); - int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + var lockkeyMsgLen = WriteSetMessage(buffer, i, "lockkey", lockKeyString, null); + await stream.WriteAsync(buffer.AsMemory(0, lockkeyMsgLen), cancellationToken).ConfigureAwait(false); + int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) @@ -178,9 +180,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun foreach (var command in commands.GetCommands()) { - var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue); - await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); - receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + var channelMsgLen = WriteSetMessage(buffer, i, command.Item1, command.Item2, lockKeyValue); + await stream.WriteAsync(buffer.AsMemory(0, channelMsgLen), cancellationToken).ConfigureAwait(false); + receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) @@ -191,10 +193,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } var targetValue = string.Format(CultureInfo.InvariantCulture, "rtp://{0}:{1}", localIp, localPort); - var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue); + var targetMsgLen = WriteSetMessage(buffer, i, "target", targetValue, lockKeyValue); - await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false); - receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + await stream.WriteAsync(buffer.AsMemory(0, targetMsgLen), cancellationToken).ConfigureAwait(false); + receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) @@ -232,9 +234,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { foreach (var command in commandList) { - var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); - await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false); - int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + var channelMsgLen = WriteSetMessage(buffer, _activeTuner, command.Item1, command.Item2, _lockkey); + await stream.WriteAsync(buffer.AsMemory(0, channelMsgLen), cancellationToken).ConfigureAwait(false); + int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked if (!ParseReturnMessage(buffer, receivedBytes, out _)) @@ -265,17 +267,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var stream = client.GetStream(); - var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue); - await stream.WriteAsync(releaseTarget, 0, releaseTarget.Length).ConfigureAwait(false); - var buffer = ArrayPool.Shared.Rent(8192); try { - await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); - var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", lockKeyValue); + var releaseTargetLen = WriteSetMessage(buffer, _activeTuner, "target", "none", lockKeyValue); + await stream.WriteAsync(buffer.AsMemory(0, releaseTargetLen)).ConfigureAwait(false); + + await stream.ReadAsync(buffer).ConfigureAwait(false); + var releaseKeyMsgLen = WriteSetMessage(buffer, _activeTuner, "lockkey", "none", lockKeyValue); _lockkey = null; - await stream.WriteAsync(releaseKeyMsg, 0, releaseKeyMsg.Length).ConfigureAwait(false); - await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + await stream.WriteAsync(buffer.AsMemory(0, releaseKeyMsgLen)).ConfigureAwait(false); + await stream.ReadAsync(buffer).ConfigureAwait(false); } finally { @@ -283,105 +285,65 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - private static byte[] CreateGetMessage(int tuner, string name) + internal static int WriteGetMessage(Span buffer, int tuner, string name) { - var byteName = Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}\0", tuner, name)); - int messageLength = byteName.Length + 10; // 4 bytes for header + 4 bytes for crc + 2 bytes for tag name and length - - var message = new byte[messageLength]; - - int offset = InsertHeaderAndName(byteName, messageLength, message); - - bool flipEndian = BitConverter.IsLittleEndian; + var byteName = string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}", tuner, name); + int offset = WriteHeaderAndName(buffer, byteName); // calculate crc and insert at the end of the message - var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4)); - if (flipEndian) - { - Array.Reverse(crcBytes); - } + var crc = Crc32.Compute(buffer.Slice(0, offset)); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), crc); - Buffer.BlockCopy(crcBytes, 0, message, offset, 4); - - return message; + return offset + 4; } - private static byte[] CreateSetMessage(int tuner, string name, string value, uint? lockkey) + private static int WriteSetMessage(Span buffer, int tuner, string name, string value, uint? lockkey) { - var byteName = Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}\0", tuner, name)); - var byteValue = Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\0", value)); + var byteName = string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}", tuner, name); + int offset = WriteHeaderAndName(buffer, byteName); + + buffer[offset++] = GetSetValue; + offset += WriteNullTerminatedString(buffer.Slice(offset), value); - int messageLength = byteName.Length + byteValue.Length + 12; if (lockkey.HasValue) { - messageLength += 6; - } - - var message = new byte[messageLength]; - - int offset = InsertHeaderAndName(byteName, messageLength, message); - - bool flipEndian = BitConverter.IsLittleEndian; - - message[offset++] = GetSetValue; - message[offset++] = Convert.ToByte(byteValue.Length); - Buffer.BlockCopy(byteValue, 0, message, offset, byteValue.Length); - offset += byteValue.Length; - if (lockkey.HasValue) - { - message[offset++] = GetSetLockkey; - message[offset++] = 4; - var lockKeyBytes = BitConverter.GetBytes(lockkey.Value); - if (flipEndian) - { - Array.Reverse(lockKeyBytes); - } - - Buffer.BlockCopy(lockKeyBytes, 0, message, offset, 4); + buffer[offset++] = GetSetLockkey; + buffer[offset++] = 4; + BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), lockkey.Value); offset += 4; } // calculate crc and insert at the end of the message - var crcBytes = BitConverter.GetBytes(HdHomerunCrc.GetCrc32(message, messageLength - 4)); - if (flipEndian) - { - Array.Reverse(crcBytes); - } + var crc = Crc32.Compute(buffer.Slice(0, offset)); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), crc); - Buffer.BlockCopy(crcBytes, 0, message, offset, 4); - - return message; + return offset + 4; } - private static int InsertHeaderAndName(byte[] byteName, int messageLength, byte[] message) + internal static int WriteNullTerminatedString(Span buffer, ReadOnlySpan value) { - // check to see if we need to flip endiannes - bool flipEndian = BitConverter.IsLittleEndian; - int offset = 0; + int len = Encoding.UTF8.GetBytes(value, buffer.Slice(1)) + 1; - // create header bytes - var getSetBytes = BitConverter.GetBytes(GetSetRequest); - var msgLenBytes = BitConverter.GetBytes((ushort)(messageLength - 8)); // Subtrace 4 bytes for header and 4 bytes for crc + // Write length in front of value + buffer[0] = Convert.ToByte(len); - if (flipEndian) - { - Array.Reverse(getSetBytes); - Array.Reverse(msgLenBytes); - } + // null-terminate + buffer[len++] = 0; + return len; + } + + private static int WriteHeaderAndName(Span buffer, ReadOnlySpan payload) + { // insert header bytes into message - Buffer.BlockCopy(getSetBytes, 0, message, offset, 2); - offset += 2; - Buffer.BlockCopy(msgLenBytes, 0, message, offset, 2); - offset += 2; + BinaryPrimitives.WriteUInt16BigEndian(buffer, GetSetRequest); + int offset = 2; + // Subtract 4 bytes for header and 4 bytes for crc + BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset), (ushort)(payload.Length + 2)); // insert tag name and length - message[offset++] = GetSetName; - message[offset++] = Convert.ToByte(byteName.Length); - - // insert name string - Buffer.BlockCopy(byteName, 0, message, offset, byteName.Length); - offset += byteName.Length; + buffer[offset++] = GetSetName; + offset += WriteNullTerminatedString(buffer.Slice(offset), payload); return offset; } @@ -442,90 +404,5 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun returnVal = Encoding.UTF8.GetString(buf, offset, valueLength - 1); // remove null terminator return true; } - - private static class HdHomerunCrc - { - private static uint[] crc_table = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; - - public static uint GetCrc32(byte[] bytes, int numBytes) - { - var hash = 0xffffffff; - for (var i = 0; i < numBytes; i++) - { - hash = (hash >> 8) ^ crc_table[(hash ^ bytes[i]) & 0xff]; - } - - var tmp = ~hash & 0xffffffff; - var b0 = tmp & 0xff; - var b1 = (tmp >> 8) & 0xff; - var b2 = (tmp >> 16) & 0xff; - var b3 = (tmp >> 24) & 0xff; - return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - } - } } } diff --git a/MediaBrowser.Common/Crc32.cs b/MediaBrowser.Common/Crc32.cs new file mode 100644 index 000000000..599eb4c99 --- /dev/null +++ b/MediaBrowser.Common/Crc32.cs @@ -0,0 +1,89 @@ +#pragma warning disable CS1591 + +using System; + +namespace MediaBrowser.Common +{ + public static class Crc32 + { + private static readonly uint[] _crcTable = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + public static uint Compute(ReadOnlySpan bytes) + { + var crc = 0xffffffff; + var len = bytes.Length; + for (var i = 0; i < len; i++) + { + crc = (crc >> 8) ^ _crcTable[(bytes[i] ^ crc) & 0xff]; + } + + return ~crc; + } + } +} diff --git a/tests/Jellyfin.Common.Tests/Crc32Tests.cs b/tests/Jellyfin.Common.Tests/Crc32Tests.cs new file mode 100644 index 000000000..d93fb839c --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Crc32Tests.cs @@ -0,0 +1,23 @@ +using System; +using System.Text; +using MediaBrowser.Common; +using Xunit; + +namespace Jellyfin.Common.Tests +{ + public static class Crc32Tests + { + [Fact] + public static void Compute_Empty_Zero() + { + Assert.Equal(0, Crc32.Compute(Array.Empty())); + } + + [Theory] + [InlineData(0x414fa339, "The quick brown fox jumps over the lazy dog")] + public static void Compute_Valid_Success(uint expected, string data) + { + Assert.Equal(expected, Crc32.Compute(Encoding.UTF8.GetBytes(data))); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs new file mode 100644 index 000000000..15cc12d64 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs @@ -0,0 +1,39 @@ +using System; +using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.LiveTv +{ + public class HdHomerunManagerTests + { + [Fact] + public void WriteNullTerminatedString_Empty_Success() + { + ReadOnlySpan expected = stackalloc byte[] + { + 1, 0 + }; + + Span buffer = stackalloc byte[128]; + int len = HdHomerunManager.WriteNullTerminatedString(buffer, string.Empty); + + Assert.Equal(expected.Length, len); + Assert.True(expected.SequenceEqual(buffer.Slice(0, len))); + } + + [Fact] + public void WriteNullTerminatedString_Valid_Success() + { + ReadOnlySpan expected = stackalloc byte[] + { + 10, (byte)'T', (byte)'h', (byte)'e', (byte)' ', (byte)'q', (byte)'u', (byte)'i', (byte)'c', (byte)'k', 0 + }; + + Span buffer = stackalloc byte[128]; + int len = HdHomerunManager.WriteNullTerminatedString(buffer, "The quick"); + + Assert.Equal(expected.Length, len); + Assert.True(expected.SequenceEqual(buffer.Slice(0, len))); + } + } +} From e1bc322b709756529759fbb74cb003e133f30ab6 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 12 Feb 2021 18:35:54 +0100 Subject: [PATCH 007/402] Add test for WriteGetMessage --- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 51 +++++++++++-------- .../LiveTv/HdHomerunManagerTests.cs | 19 +++++++ 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 188eca158..20a4d87fb 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -288,19 +288,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun internal static int WriteGetMessage(Span buffer, int tuner, string name) { var byteName = string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}", tuner, name); - int offset = WriteHeaderAndName(buffer, byteName); - - // calculate crc and insert at the end of the message - var crc = Crc32.Compute(buffer.Slice(0, offset)); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), crc); - - return offset + 4; + int offset = WriteHeaderAndPayload(buffer, byteName); + return FinishPacket(buffer, offset); } private static int WriteSetMessage(Span buffer, int tuner, string name, string value, uint? lockkey) { var byteName = string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}", tuner, name); - int offset = WriteHeaderAndName(buffer, byteName); + int offset = WriteHeaderAndPayload(buffer, byteName); buffer[offset++] = GetSetValue; offset += WriteNullTerminatedString(buffer.Slice(offset), value); @@ -313,17 +308,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun offset += 4; } - // calculate crc and insert at the end of the message - var crc = Crc32.Compute(buffer.Slice(0, offset)); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), crc); - - return offset + 4; + return FinishPacket(buffer, offset); } - internal static int WriteNullTerminatedString(Span buffer, ReadOnlySpan value) + internal static int WriteNullTerminatedString(Span buffer, ReadOnlySpan payload) { - int len = Encoding.UTF8.GetBytes(value, buffer.Slice(1)) + 1; + int len = Encoding.UTF8.GetBytes(payload, buffer.Slice(1)) + 1; + // TODO: variable length: this can be 2 bytes if len > 127 // Write length in front of value buffer[0] = Convert.ToByte(len); @@ -333,21 +325,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return len; } - private static int WriteHeaderAndName(Span buffer, ReadOnlySpan payload) + private static int WriteHeaderAndPayload(Span buffer, ReadOnlySpan payload) { - // insert header bytes into message + // Packet type BinaryPrimitives.WriteUInt16BigEndian(buffer, GetSetRequest); - int offset = 2; - // Subtract 4 bytes for header and 4 bytes for crc - BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset), (ushort)(payload.Length + 2)); - // insert tag name and length + // We write the payload length at the end + int offset = 4; + + // Tag buffer[offset++] = GetSetName; - offset += WriteNullTerminatedString(buffer.Slice(offset), payload); + + // Payload length + data + int strLen = WriteNullTerminatedString(buffer.Slice(offset), payload); + offset += strLen; return offset; } + private static int FinishPacket(Span buffer, int offset) + { + // Payload length + BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(2), (ushort)(offset - 4)); + + // calculate crc and insert at the end of the message + var crc = Crc32.Compute(buffer.Slice(0, offset)); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), crc); + + return offset + 4; + } + private static bool ParseReturnMessage(byte[] buf, int numBytes, out string returnVal) { returnVal = string.Empty; diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs index 15cc12d64..7e04a1ec1 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs @@ -35,5 +35,24 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Assert.Equal(expected.Length, len); Assert.True(expected.SequenceEqual(buffer.Slice(0, len))); } + + [Fact] + public void WriteGetMessage_Valid_Success() + { + ReadOnlySpan expected = stackalloc byte[] + { + 0, 4, + 0, 12, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 0xc0, 0xc9, 0x87, 0x33 + }; + + Span buffer = stackalloc byte[128]; + int len = HdHomerunManager.WriteGetMessage(buffer, 0, "N"); + + Assert.Equal(expected.Length, len); + Assert.True(expected.SequenceEqual(buffer.Slice(0, len))); + } } } From dc9e2ad1a4a21677cb22d3a2c76dee40859462bb Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Feb 2021 11:46:38 +0100 Subject: [PATCH 008/402] Add some more Crc32 tests --- tests/Jellyfin.Common.Tests/Crc32Tests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Jellyfin.Common.Tests/Crc32Tests.cs b/tests/Jellyfin.Common.Tests/Crc32Tests.cs index d93fb839c..e95a2867f 100644 --- a/tests/Jellyfin.Common.Tests/Crc32Tests.cs +++ b/tests/Jellyfin.Common.Tests/Crc32Tests.cs @@ -19,5 +19,15 @@ namespace Jellyfin.Common.Tests { Assert.Equal(expected, Crc32.Compute(Encoding.UTF8.GetBytes(data))); } + + [Theory] + [InlineData(0x414fa339, "54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F67")] + [InlineData(0x190a55ad, "0000000000000000000000000000000000000000000000000000000000000000")] + [InlineData(0xff6cab0b, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")] + [InlineData(0x91267e8a, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")] + public static void Compute_ValidHex_Success(uint expected, string data) + { + Assert.Equal(expected, Crc32.Compute(Convert.FromHexString(data))); + } } } From aff0aea60fc52a2253f04749f11bcb02f6e1f67c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 23 Feb 2021 14:14:02 +0100 Subject: [PATCH 009/402] Improve branch coverage --- .../Models/LiveTvDtos/GetProgramsDto.cs | 1 - .../JsonOmdbNotAvailableInt32Converter.cs | 2 +- .../TestPluginWithoutPages.cs | 27 ++++++++++ .../Json/JsonCommaDelimitedArrayTests.cs | 52 ++++++++++++++++++- .../Json/JsonOmdbConverterTests.cs | 27 +++++----- 5 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 tests/Jellyfin.Api.Tests/TestPluginWithoutPages.cs diff --git a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs index 588ce717c..8913180e4 100644 --- a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs +++ b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using Jellyfin.Data.Enums; using MediaBrowser.Common.Json.Converters; diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs index cb3d83f58..3d97a9de5 100644 --- a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Common.Json.Converters return (int?)converter.ConvertFromString(str); } - return JsonSerializer.Deserialize(ref reader, options); + return JsonSerializer.Deserialize(ref reader, options); } /// diff --git a/tests/Jellyfin.Api.Tests/TestPluginWithoutPages.cs b/tests/Jellyfin.Api.Tests/TestPluginWithoutPages.cs new file mode 100644 index 000000000..2d2f78a98 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/TestPluginWithoutPages.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using System; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; + +namespace Jellyfin.Api.Tests +{ + public class TestPluginWithoutPages : BasePlugin + { + public TestPluginWithoutPages(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + + public static TestPluginWithoutPages? Instance { get; private set; } + + public override Guid Id => new Guid("ae95cbe6-bd3d-4d73-8596-490db334611e"); + + public override string Name => nameof(TestPluginWithoutPages); + + public override string Description => "Server test Plugin without web pages."; + } +} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs index 0d2bdd1af..ca300401d 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System; +using System.Text.Json; using System.Text.Json.Serialization; using Jellyfin.Common.Tests.Models; using MediaBrowser.Model.Session; @@ -8,6 +9,27 @@ namespace Jellyfin.Common.Tests.Json { public static class JsonCommaDelimitedArrayTests { + [Fact] + public static void Deserialize_String_Null_Success() + { + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": null }", options); + Assert.Null(value?.Value); + } + + [Fact] + public static void Deserialize_Empty_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = Array.Empty() + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": """" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + [Fact] public static void Deserialize_String_Valid_Success() { @@ -48,6 +70,34 @@ namespace Jellyfin.Common.Tests.Json Assert.Equal(desiredValue.Value, value?.Value); } + [Fact] + public static void Deserialize_GenericCommandType_EmptyEntry_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,,MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Invalid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,TotallyNotAVallidCommand,MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + [Fact] public static void Deserialize_GenericCommandType_Space_Valid_Success() { diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs index faed086a1..efe8063a0 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs @@ -38,6 +38,15 @@ namespace Jellyfin.Common.Tests.Json Assert.Null(result); } + [Theory] + [InlineData("\"8\"", 8)] + [InlineData("8", 8)] + public void Deserialize_NullableInt_Success(string input, int? expected) + { + var result = JsonSerializer.Deserialize(input, _options); + Assert.Equal(result, expected); + } + [Theory] [InlineData("\"N/A\"")] [InlineData("null")] @@ -48,21 +57,11 @@ namespace Jellyfin.Common.Tests.Json } [Theory] - [InlineData("\"8\"", 8)] - [InlineData("8", 8)] - public void Deserialize_Int_Success(string input, int expected) + [InlineData("\"Jellyfin\"", "Jellyfin")] + public void Deserialize_Normal_String_Success(string input, string expected) { - var result = JsonSerializer.Deserialize(input, _options); - Assert.Equal(result, expected); - } - - [Fact] - public void Deserialize_Normal_String_Success() - { - const string Input = "\"Jellyfin\""; - const string Expected = "Jellyfin"; - var result = JsonSerializer.Deserialize(Input, _options); - Assert.Equal(Expected, result); + var result = JsonSerializer.Deserialize(input, _options); + Assert.Equal(expected, result); } [Fact] From acac21d8dc3eb9383136ff692606dc2f65adf405 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 23 Feb 2021 16:45:10 +0100 Subject: [PATCH 010/402] Improve tests --- .../EntryPoints/UdpServerEntryPoint.cs | 11 + Emby.Server.Implementations/Udp/UdpServer.cs | 2 +- .../JsonCommaDelimitedArrayConverter.cs | 2 +- .../JsonPipeDelimitedArrayConverter.cs | 2 +- .../Json/Converters/JsonVersionConverter.cs | 20 - MediaBrowser.Common/Json/JsonDefaults.cs | 1 - .../Test Data/Updates/manifest-stable.json | 684 ++++++++++++++++++ .../Updates/InstallationManagerTests.cs | 57 ++ 8 files changed, 755 insertions(+), 24 deletions(-) delete mode 100644 MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index a12a6b26c..3624e079f 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -1,5 +1,6 @@ #nullable enable +using System; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; @@ -51,6 +52,8 @@ namespace Emby.Server.Implementations.EntryPoints /// public Task RunAsync() { + CheckDisposed(); + try { _udpServer = new UdpServer(_logger, _appHost, _config); @@ -64,6 +67,14 @@ namespace Emby.Server.Implementations.EntryPoints return Task.CompletedTask; } + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(this.GetType().Name); + } + } + /// public void Dispose() { diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 4fd7ac0c1..d01184e0b 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.Udp /// Starts the specified port. /// /// The port. - /// + /// The cancellation token to cancel operation. public void Start(int port, CancellationToken cancellationToken) { _endpoint = new IPEndPoint(IPAddress.Any, port); diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs index 38a7e1d20..d9f6519e9 100644 --- a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Common.Json.Converters /// public override void Write(Utf8JsonWriter writer, T[] value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value, options); + throw new NotImplementedException(); } } } diff --git a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs index 377db1a44..c408a3be1 100644 --- a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Common.Json.Converters /// public override void Write(Utf8JsonWriter writer, T[] value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value, options); + throw new NotImplementedException(); } } } diff --git a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs deleted file mode 100644 index 37e6f64e3..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a Version object or value to/from JSON. - /// - public class JsonVersionConverter : JsonConverter - { - /// - public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => new Version(reader.GetString()); - - /// - public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); - } -} diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 2ef24a884..a3999e7e2 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -35,7 +35,6 @@ namespace MediaBrowser.Common.Json { new JsonGuidConverter(), new JsonNullableGuidConverter(), - new JsonVersionConverter(), new JsonStringEnumConverter(), new JsonNullableStructConverterFactory(), new JsonBoolNumberConverter(), diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json new file mode 100644 index 000000000..b766e668e --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json @@ -0,0 +1,684 @@ +[ + { + "guid": "a4df60c5-6ab4-412a-8f79-2cab93fb2bc5", + "name": "Anime", + "description": "Manage your anime in Jellyfin. This plugin supports several different metadata providers and options for organizing your collection.\n", + "overview": "Manage your anime from Jellyfin", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "10.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/anime/anime_10.0.0.0.zip", + "checksum": "93e969adeba1050423fc8817ed3c36f8", + "timestamp": "2020-08-17T01:41:13Z" + }, + { + "version": "9.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/anime/anime_9.0.0.0.zip", + "checksum": "9b1cebff835813e15f414f44b40c41c8", + "timestamp": "2020-07-20T01:30:16Z" + } + ] + }, + { + "guid": "70b7b43b-471b-4159-b4be-56750c795499", + "name": "Auto Organize", + "description": "Automatically organize your media", + "overview": "Automatically organize your media", + "owner": "jellyfin", + "category": "General", + "versions": [ + { + "version": "9.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/auto-organize/auto-organize_9.0.0.0.zip", + "checksum": "ff29ac3cbe05d208b6af94cd6d9dea39", + "timestamp": "2020-12-05T22:31:12Z" + }, + { + "version": "8.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/auto-organize/auto-organize_8.0.0.0.zip", + "checksum": "460bbb45e556464a8476b18e41c097f5", + "timestamp": "2020-07-20T01:30:25Z" + } + ] + }, + { + "guid": "9c4e63f1-031b-4f25-988b-4f7d78a8b53e", + "name": "Bookshelf", + "description": "Supports several different metadata providers and options for organizing your collection.\n", + "overview": "Manage your books", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "5.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/bookshelf/bookshelf_5.0.0.0.zip", + "checksum": "2063fb8ab317b8d77b200fde41eb5e1e", + "timestamp": "2020-12-05T22:03:13Z" + }, + { + "version": "4.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/bookshelf/bookshelf_4.0.0.0.zip", + "checksum": "fc9f76c0815d766491e5b0f30ede55ed", + "timestamp": "2020-07-20T01:30:33Z" + } + ] + }, + { + "guid": "cfa0f7f4-4155-4d71-849b-d6598dc4c5bb", + "name": "Email", + "description": "Send SMTP email notifications", + "overview": "Send SMTP email notifications", + "owner": "jellyfin", + "category": "Notifications", + "versions": [ + { + "version": "9.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/email/email_9.0.0.0.zip", + "checksum": "cfe7afc00f3fbd6d6ab8244d7ff968ce", + "timestamp": "2020-12-05T22:20:32Z" + }, + { + "version": "7.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/email/email_7.0.0.0.zip", + "checksum": "680ca511d8ad84923cb04f024fd8eb19", + "timestamp": "2020-07-20T01:30:40Z" + } + ] + }, + { + "guid": "170a157f-ac6c-437a-abdd-ca9c25cebd39", + "name": "Fanart", + "description": "Scrape poster images for movies, shows, and artists in your library.", + "overview": "Scrape poster images from Fanart", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/fanart/fanart_6.0.0.0.zip", + "checksum": "ee4360bfcc8722d5a3a54cfe7eef640f", + "timestamp": "2020-12-05T22:25:43Z" + }, + { + "version": "5.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/fanart/fanart_5.0.0.0.zip", + "checksum": "f842f7d65d23f377761c907d40b89647", + "timestamp": "2020-07-20T01:30:48Z" + } + ] + }, + { + "guid": "e29621a5-fa9e-4330-982e-ef6e54c0cad2", + "name": "Gotify Notification", + "description": "You must have a Gotify server to use this plugin!\n", + "overview": "Sends notifications to your Gotify server", + "owner": "crobibero", + "category": "Notifications", + "versions": [ + { + "version": "7.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/gotify-notification/gotify-notification_7.0.0.0.zip", + "checksum": "7c5ff9e8792c8cdee7e8a2aaeb6cc093", + "timestamp": "2020-07-20T01:30:56Z" + } + ] + }, + { + "guid": "a59b5c4b-05a8-488f-bfa8-7a63fffc7639", + "name": "IPTV", + "description": "Enable IPTV support in Jellyfin", + "overview": "Enable IPTV support in Jellyfin", + "owner": "jellyfin", + "category": "Channel", + "versions": [ + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/iptv/iptv_6.0.0.0.zip", + "checksum": "9cf103bf67a4eda7c3a42d9b235f6447", + "timestamp": "2020-07-20T01:31:05Z" + } + ] + }, + { + "guid": "4682DD4C-A675-4F1B-8E7C-79ADF137A8F8", + "name": "ISO Mounter", + "description": "Mount your ISO files for Jellyfin.\n", + "overview": "Mount your ISO files for Jellyfin", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "1.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/iso-mounter/iso-mounter_1.0.0.0.zip", + "checksum": "847e5bc7ac34c1bf4dc5b28173170fae", + "timestamp": "2020-07-20T01:31:13Z" + } + ] + }, + { + "guid": "771e19d6-5385-4caf-b35c-28a0e865cf63", + "name": "Kodi Sync Queue", + "description": "This plugin will track all media changes while Kodi clients are offline to decrease sync times.", + "overview": "Sync all media changes with Kodi clients", + "owner": "jellyfin", + "category": "General", + "versions": [ + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/kodi-sync-queue/kodi-sync-queue_6.0.0.0.zip", + "checksum": "787c856c0d2ad2224cdd8b3094cf0329", + "timestamp": "2020-12-05T22:10:37Z" + }, + { + "version": "5.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/kodi-sync-queue/kodi-sync-queue_5.0.0.0.zip", + "checksum": "08285397aecd93ea64a4f15d38b1bd7b", + "timestamp": "2020-07-20T01:31:22Z" + } + ] + }, + { + "guid": "958aad66-3784-4d2a-b89a-a7b6fab6e25c", + "name": "LDAP Authentication", + "description": "Authenticate your Jellyfin users against an LDAP database, and optionally create users who do not yet exist automatically.\nAllows the administrator to customize most aspects of the LDAP authentication process, including customizable search attributes, username attribute, and a search filter for administrative users (set on user creation). The user, via the \"Manual Login\" process, can enter any valid attribute value, which will be mapped back to the specified username attribute automatically as well.\n", + "overview": "Authenticate users against an LDAP database", + "owner": "jellyfin", + "category": "Authentication", + "versions": [ + { + "version": "10.0.0.0", + "changelog": "Update for 10.7 support\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/ldap-authentication/ldap-authentication_10.0.0.0.zip", + "checksum": "62e7e1cd3ffae0944c14750a3c90df4f", + "timestamp": "2020-12-05T19:48:10Z" + }, + { + "version": "9.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/ldap-authentication/ldap-authentication_9.0.0.0.zip", + "checksum": "7f2f83587a65a43ebf168e4058421463", + "timestamp": "2020-07-22T15:42:57Z" + }, + { + "version": "8.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/ldap-authentication/ldap-authentication_8.0.0.0.zip", + "checksum": "8af8cee62717d63577f8b1e710839415", + "timestamp": "2020-07-20T01:31:30Z" + } + ] + }, + { + "guid": "9574ac10-bf23-49bc-949f-924f23cfa48f", + "name": "NextPVR", + "description": "Provides access to live TV, program guide, and recordings from NextPVR.\n", + "overview": "Live TV plugin for NextPVR", + "owner": "jellyfin", + "category": "LiveTV", + "versions": [ + { + "version": "5.0.0.0", + "changelog": "Updated to use NextPVR API v5, no longer compatable with API v4.\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/nextpvr/nextpvr_5.0.0.0.zip", + "checksum": "d70f694d14bf9462ba2b2ebe110068d3", + "timestamp": "2020-12-05T22:24:03Z" + }, + { + "version": "4.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/nextpvr/nextpvr_4.0.0.0.zip", + "checksum": "b15949d895ac5a8c89496581db350478", + "timestamp": "2020-07-20T01:31:38Z" + } + ] + }, + { + "guid": "4b9ed42f-5185-48b5-9803-6ff2989014c4", + "name": "Open Subtitles", + "description": "Download subtitles from the internet to use with your media files.", + "overview": "Download subtitles for your media", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "10.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/open-subtitles/open-subtitles_10.0.0.0.zip", + "checksum": "ed99d03ec463bf15fca1256a113f57b4", + "timestamp": "2020-12-05T21:56:19Z" + }, + { + "version": "9.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/open-subtitles/open-subtitles_9.0.0.0.zip", + "checksum": "16789b26497cea0509daf6b18c579340", + "timestamp": "2020-07-20T01:32:00Z" + } + ] + }, + { + "guid": "5c534381-91a3-43cb-907a-35aa02eb9d2c", + "name": "Playback Reporting", + "description": "Collect and show user play statistics", + "overview": "Collect and show user play statistics", + "owner": "jellyfin", + "category": "General", + "versions": [ + { + "version": "9.0.0.0", + "changelog": "Add authentication to plugin endpoints\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/playback-reporting/playback-reporting_9.0.0.0.zip", + "checksum": "ca323b3dcb2cb86cc2e72a7a0f1eee22", + "timestamp": "2020-12-05T22:15:48Z" + }, + { + "version": "8.0.0.0", + "changelog": "Add authentication to plugin endpoints\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/playback-reporting/playback-reporting_8.0.0.0.zip", + "checksum": "58644c505586542ef0b8b65e2f704bd1", + "timestamp": "2020-11-18T03:01:51Z" + }, + { + "version": "7.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/playback-reporting/playback-reporting_7.0.0.0.zip", + "checksum": "6a361ef33bca97f9155856d02ff47380", + "timestamp": "2020-07-20T01:32:09Z" + } + ] + }, + { + "guid": "de228f12-e43e-4bd9-9fc0-2830819c3b92", + "name": "Pushbullet", + "description": "Get notifications via Pushbullet.\n", + "overview": "Pushbullet notification plugin", + "owner": "jellyfin", + "category": "Notifications", + "versions": [ + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/pushbullet/pushbullet_6.0.0.0.zip", + "checksum": "248cf3d56644f1d909e75aaddbdfb3a6", + "timestamp": "2020-12-06T02:47:53Z" + }, + { + "version": "5.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/pushbullet/pushbullet_5.0.0.0.zip", + "checksum": "dabbdd86328b2922a69dfa0c9e1c8343", + "timestamp": "2020-07-20T01:32:17Z" + } + ] + }, + { + "guid": "F240D6BE-5743-441B-87F1-A70ECAC42642", + "name": "Pushover", + "description": "Send messages to a wide range of devices through Pushover.", + "overview": "Send notifications via Pushover", + "owner": "crobibero", + "category": "Notifications", + "versions": [ + { + "version": "4.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/pushover/pushover_4.0.0.0.zip", + "checksum": "56a0da16c7e48cc184987737b7e155dd", + "timestamp": "2020-07-20T01:32:25Z" + } + ] + }, + { + "guid": "d4312cd9-5c90-4f38-82e8-51da566790e8", + "name": "Reports", + "description": "Generate reports of your media library", + "overview": "Generate reports of your media library", + "owner": "jellyfin", + "category": "General", + "versions": [ + { + "version": "11.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/reports/reports_11.0.0.0.zip", + "checksum": "d71bc6a4c008e58ee70ad44c83bfd310", + "timestamp": "2020-12-05T22:00:46Z" + }, + { + "version": "10.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/reports/reports_10.0.0.0.zip", + "checksum": "3917e75839337475b42daf2ba0b5bd7b", + "timestamp": "2020-10-19T19:30:41Z" + }, + { + "version": "9.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/reports/reports_9.0.0.0.zip", + "checksum": "5b5ad8d885616a21e8d1e8eecf5ea979", + "timestamp": "2020-10-16T23:52:37Z" + } + ] + }, + { + "guid": "1fc322a1-af2e-49a5-b2eb-a89b4240f700", + "name": "ServerWMC", + "description": "Provides access to Live TV, Program Guide and Recordings from your Windows MediaCenter Server running ServerWMC. Requires ServerWMC to be installed and running on your Windows MediaCenter machine.\n", + "overview": "Jellyfin Live TV plugin for Windows MediaCenter with ServerWMC", + "owner": "jellyfin", + "category": "LiveTV", + "versions": [ + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/serverwmc/serverwmc_6.0.0.0.zip", + "checksum": "3120af0cea2c1cb8b7cf578d9b4b862c", + "timestamp": "2020-12-05T22:28:15Z" + }, + { + "version": "5.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/serverwmc/serverwmc_5.0.0.0.zip", + "checksum": "dc44b039aa1b66eaf40a44fbf02d37e2", + "timestamp": "2020-07-20T01:32:42Z" + } + ] + }, + { + "guid": "94fb77c3-55ad-4c50-bf4e-4e5497467b79", + "name": "Slack Notifications", + "description": "Get notifications via Slack.\n", + "overview": "Get notifications via Slack", + "owner": "jellyfin", + "category": "Notifications", + "versions": [ + { + "version": "7.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/slack-notifications/slack-notifications_7.0.0.0.zip", + "checksum": "1d5330a77ce7b2a9ac8e5d58088a012c", + "timestamp": "2020-12-05T22:40:02Z" + }, + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/slack-notifications/slack-notifications_6.0.0.0.zip", + "checksum": "ede4cbe064542d1ecccc5823921bee4b", + "timestamp": "2020-07-20T01:32:50Z" + } + ] + }, + { + "guid": "bc4aad2e-d3d0-4725-a5e2-fd07949e5b42", + "name": "TMDb Box Sets", + "description": "Automatically create movie box sets based on TMDb collections", + "overview": "Automatically create movie box sets based on TMDb collections", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "7.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tmdb-box-sets/tmdb-box-sets_7.0.0.0.zip", + "checksum": "1551792e6af4d36f2cead01153c73cf0", + "timestamp": "2020-12-05T22:07:21Z" + }, + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tmdb-box-sets/tmdb-box-sets_6.0.0.0.zip", + "checksum": "b92b68a922c5fcbb8f4d47b8601b01b6", + "timestamp": "2020-07-20T01:32:58Z" + } + ] + }, + { + "guid": "4fe3201e-d6ae-4f2e-8917-e12bda571281", + "name": "Trakt", + "description": "Record your watched media with Trakt.\n", + "overview": "Record your watched media with Trakt", + "owner": "jellyfin", + "category": "General", + "versions": [ + { + "version": "11.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/trakt/trakt_11.0.0.0.zip", + "checksum": "2257ccde1e39114644a27e0966a0bf2d", + "timestamp": "2020-12-05T19:56:12Z" + }, + { + "version": "10.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/trakt/trakt_10.0.0.0.zip", + "checksum": "ab67e6b59ea2e7860a6a3ff7b8452759", + "timestamp": "2020-07-20T01:33:06Z" + } + ] + }, + { + "guid": "3fd018e5-5e78-4e58-b280-a0c068febee0", + "name": "TVHeadend", + "description": "Manage TVHeadend from Jellyfin", + "overview": "Manage TVHeadend from Jellyfin", + "owner": "jellyfin", + "category": "LiveTV", + "versions": [ + { + "version": "7.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tvheadend/tvheadend_7.0.0.0.zip", + "checksum": "1abbfce737b6962f4b1b2255dc63e932", + "timestamp": "2021-01-05T16:20:33Z" + }, + { + "version": "6.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tvheadend/tvheadend_6.0.0.0.zip", + "checksum": "143c34fd70d7173b8912cc03ce4b517d", + "timestamp": "2020-07-20T01:33:15Z" + } + ] + }, + { + "guid": "022a3003-993f-45f1-8565-87d12af2e12a", + "name": "InfuseSync", + "description": "This plugin will track all media changes while any Infuse clients are offline to decrease sync times when logging back in to your server.", + "overview": "Blazing fast indexing for Infuse", + "owner": "Firecore LLC", + "category": "General", + "versions": [ + { + "version": "1.2.4.0", + "changelog": "New Playlist support.\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://github.com/firecore/InfuseSync/releases/download/v1.2.4/InfuseSync-jellyfin-1.2.4.zip", + "checksum": "7adde11b8c8404fd2923f59d98fb1a30", + "timestamp": "2020-10-12T08:00:00Z" + }, + { + "version": "1.2.1.3", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://github.com/firecore/InfuseSync/releases/download/v1.2.3/InfuseSync-jellyfin-1.2.3.zip", + "checksum": "d8e2c5fe736a302097bb3bac3d04b1c4", + "timestamp": "2020-09-18T12:19:00Z" + }, + { + "version": "1.2.1.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://github.com/firecore/InfuseSync/releases/download/v1.2.1/InfuseSync-jellyfin-1.2.1.zip", + "checksum": "1a853e926cc422f5d79d398d9ae18ee8", + "timestamp": "2020-08-21T10:48:00Z" + }, + { + "version": "1.2.0.0", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://github.com/firecore/InfuseSync/releases/download/v1.2.0/InfuseSync-jellyfin-1.2.0.zip", + "checksum": "2d3c7859852695a7f05adc6d3fcbc783", + "timestamp": "2020-07-20T11:51:00Z" + } + ] + }, + { + "guid": "8119f3c6-cfc2-4d9c-a0ba-028f1d93e526", + "name": "Cover Art Archive", + "description": "This plugin provides images from the Cover Art Archive https://musicbrainz.org/doc/Cover_Art_Archive and depends on the MusicBrainz metadata provider to know what images belong where\n", + "overview": "MusicBrainz Cover Art Archive", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "2.0.0.0", + "changelog": "changelog\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/cover-art-archive/cover-art-archive_2.0.0.0.zip", + "checksum": "bea8fa4a37b3e7ed74e22266e7597a68", + "timestamp": "2020-12-06T02:51:03Z" + }, + { + "version": "1.0.0.3", + "changelog": "changelog\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/cover-art-archive/cover-art-archive_1.0.0.3.zip", + "checksum": "c502a5c54b168810614c1c40709b9598", + "timestamp": "2020-08-06T21:21:22Z" + } + ] + }, + { + "guid": "A4A488D0-17A3-4919-8D82-7F3DE4F6B209", + "name": "TV Maze", + "description": "Get TV metadata from TV Maze\n", + "overview": "Get TV metadata from TV Maze", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "5.0.0.0", + "changelog": "Get additional image types\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tv-maze/tv-maze_5.0.0.0.zip", + "checksum": "509a85e40b1d1ac36eef45673deaf606", + "timestamp": "2020-12-06T02:51:56Z" + }, + { + "version": "4.0.0.0", + "changelog": "Get additional image types\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tv-maze/tv-maze_4.0.0.0.zip", + "checksum": "58ee9ab3f129151bdfff033ad889ad87", + "timestamp": "2020-11-24T14:44:37Z" + }, + { + "version": "3.0.0.0", + "changelog": "Remove unused dependencies \n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tv-maze/tv-maze_3.0.0.0.zip", + "checksum": "f3b2c70b3e136fb15c917e4420f4fdec", + "timestamp": "2020-11-09T14:32:56Z" + }, + { + "version": "2.0.0.0", + "changelog": "Remove unused dependencies \n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tv-maze/tv-maze_2.0.0.0.zip", + "checksum": "c7662ae8ae52ce8a4e8d685d55f36e80", + "timestamp": "2020-11-09T02:33:11Z" + }, + { + "version": "1.0.0.0", + "changelog": "Initial release.\n", + "targetAbi": "10.6.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/tv-maze/tv-maze_1.0.0.0.zip", + "checksum": "c90eee48c12f2c07880b4b28e507fd14", + "timestamp": "2020-11-08T19:05:32Z" + } + ] + }, + { + "guid": "a677c0da-fac5-4cde-941a-7134223f14c8", + "name": "TheTVDB", + "description": "Get TV metadata from TheTvdb\n", + "overview": "Get TV metadata from TheTvdb", + "owner": "jellyfin", + "category": "Metadata", + "versions": [ + { + "version": "2.0.0.0", + "changelog": "Remove from Jellyfin core.\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/thetvdb/thetvdb_2.0.0.0.zip", + "checksum": "e46cee334476a1b475e5c553171c4cb6", + "timestamp": "2020-12-16T20:03:28Z" + }, + { + "version": "1.0.0.0", + "changelog": "Remove from Jellyfin core.\n", + "targetAbi": "10.7.0.0", + "sourceUrl": "https://repo.jellyfin.org/releases/plugin/thetvdb/thetvdb_1.0.0.0.zip", + "checksum": "5a3dca5c0db4824d83bfd4e7e2b7bf11", + "timestamp": "2020-12-06T02:56:40Z" + } + ] + } +] \ No newline at end of file diff --git a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs new file mode 100644 index 000000000..4fa64d8a2 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Emby.Server.Implementations.Updates; +using MediaBrowser.Model.Updates; +using Moq; +using Moq.Protected; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Updates +{ + public class InstallationManagerTests + { + private readonly Fixture _fixture; + private readonly InstallationManager _installationManager; + + public InstallationManagerTests() + { + var messageHandler = new Mock(); + messageHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .Returns( + (m, _) => + { + return Task.FromResult(new HttpResponseMessage() + { + Content = new StreamContent(File.OpenRead("Test Data/Updates/" + m.RequestUri?.Segments[^1])) + }); + }); + + var http = new Mock(); + http.Setup(x => x.CreateClient(It.IsAny())) + .Returns(new HttpClient(messageHandler.Object)); + _fixture = new Fixture(); + _fixture.Customize(new AutoMoqCustomization + { + ConfigureMembers = true + }).Inject(http); + _installationManager = _fixture.Create(); + } + + [Fact] + public async Task GetPackages_Valid_Success() + { + IList packages = await _installationManager.GetPackages( + "Jellyfin Stable", + "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", + false); + + Assert.Equal(25, packages.Count); + } + } +} From 032d72a8a7a1c3884d07b0f3b7bd93790988d414 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 23 Feb 2021 17:30:24 +0100 Subject: [PATCH 011/402] Pls fix race condition --- Emby.Server.Implementations/ApplicationHost.cs | 10 ++++++---- Jellyfin.Server/Program.cs | 2 +- tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0a7c5c1fb..d7ac8bc3b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -126,7 +126,6 @@ namespace Emby.Server.Implementations private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; private string[] _urlPrefixes; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); /// /// Gets a value indicating whether this instance can self restart. @@ -484,8 +483,9 @@ namespace Emby.Server.Implementations /// Runs the startup tasks. /// /// . - public async Task RunStartupTasksAsync() + public async Task RunStartupTasksAsync(CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); Logger.LogInformation("Running startup tasks"); Resolve().AddTasks(GetExports(false)); @@ -501,13 +501,15 @@ namespace Emby.Server.Implementations var stopWatch = new Stopwatch(); stopWatch.Start(); - await Task.WhenAll(StartEntryPoints(entryPoints, true)).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + await Task.WhenAny(Task.WhenAll(StartEntryPoints(entryPoints, true)), Task.Delay(-1, cancellationToken)).ConfigureAwait(false); Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); Logger.LogInformation("Core startup complete"); CoreStartupHasCompleted = true; stopWatch.Restart(); - await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + await Task.WhenAny(Task.WhenAll(StartEntryPoints(entryPoints, false)), Task.Delay(-1, cancellationToken)).ConfigureAwait(false); Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed); stopWatch.Stop(); } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index f05cdfe9b..81199c775 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -202,7 +202,7 @@ namespace Jellyfin.Server throw; } - await appHost.RunStartupTasksAsync().ConfigureAwait(false); + await appHost.RunStartupTasksAsync(_tokenSource.Token).ConfigureAwait(false); stopWatch.Stop(); diff --git a/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs index dbbd5ac28..ab7e0b6e7 100644 --- a/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.IO; +using System.Threading; using Emby.Server.Implementations; using Emby.Server.Implementations.IO; using Jellyfin.Server; @@ -21,7 +22,7 @@ namespace Jellyfin.Api.Tests public class JellyfinApplicationFactory : WebApplicationFactory { private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); - private static readonly ConcurrentBag _disposableComponents = new ConcurrentBag(); + private readonly ConcurrentBag _disposableComponents = new ConcurrentBag(); /// /// Initializes a new instance of the class. @@ -96,7 +97,7 @@ namespace Jellyfin.Api.Tests var appHost = (TestAppHost)testServer.Services.GetRequiredService(); appHost.ServiceProvider = testServer.Services; appHost.InitializeServices().GetAwaiter().GetResult(); - appHost.RunStartupTasksAsync().GetAwaiter().GetResult(); + appHost.RunStartupTasksAsync(CancellationToken.None).GetAwaiter().GetResult(); return testServer; } From 2bc1eef4ddb23385f90c8bfc6af2ecf9888d7879 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 24 Feb 2021 22:18:59 +0100 Subject: [PATCH 012/402] Clean up code --- Emby.Server.Implementations/ApplicationHost.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index d7ac8bc3b..b1c20815e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -499,17 +499,22 @@ namespace Emby.Server.Implementations var entryPoints = GetExports(); + cancellationToken.ThrowIfCancellationRequested(); + var stopWatch = new Stopwatch(); stopWatch.Start(); - cancellationToken.ThrowIfCancellationRequested(); - await Task.WhenAny(Task.WhenAll(StartEntryPoints(entryPoints, true)), Task.Delay(-1, cancellationToken)).ConfigureAwait(false); + + await Task.WhenAll(StartEntryPoints(entryPoints, true)).ConfigureAwait(false); Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); Logger.LogInformation("Core startup complete"); CoreStartupHasCompleted = true; - stopWatch.Restart(); + cancellationToken.ThrowIfCancellationRequested(); - await Task.WhenAny(Task.WhenAll(StartEntryPoints(entryPoints, false)), Task.Delay(-1, cancellationToken)).ConfigureAwait(false); + + stopWatch.Restart(); + + await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed); stopWatch.Stop(); } From 03cc6b1d782067e58975dbea4b61e42a3c04e1aa Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 25 Feb 2021 19:02:27 -0500 Subject: [PATCH 013/402] Make styling more consistent --- Jellyfin.Server.Implementations/Users/UserManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 38a074e98..25d7524c7 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -407,12 +407,12 @@ namespace Jellyfin.Server.Implementations.Users var authenticationProvider = authResult.authenticationProvider; var success = authResult.success; - if (user is null) + if (user == null) { string updatedUsername = authResult.username; if (success - && authenticationProvider is not null + && authenticationProvider != null && authenticationProvider is not DefaultAuthenticationProvider) { // Trust the username returned by the authentication provider From 02848189e3e2824fac6ee155f3efa52084279b18 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 28 Feb 2021 00:10:36 +0100 Subject: [PATCH 014/402] MaybeNullWhen(false) -> NotNullWhen(true) --- MediaBrowser.Model/Entities/ProviderIdsExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 4aff6e3a4..11c3dbe42 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Model.Entities /// The name. /// The provider id. /// true if a provider id with the given name was found; otherwise false. - public static bool TryGetProviderId(this IHasProviderIds instance, string name, [MaybeNullWhen(false)] out string id) + public static bool TryGetProviderId(this IHasProviderIds instance, string name, [NotNullWhen(true)] out string? id) { if (instance == null) { @@ -39,7 +39,7 @@ namespace MediaBrowser.Model.Entities /// The provider. /// The provider id. /// true if a provider id with the given name was found; otherwise false. - public static bool TryGetProviderId(this IHasProviderIds instance, MetadataProvider provider, [MaybeNullWhen(false)] out string id) + public static bool TryGetProviderId(this IHasProviderIds instance, MetadataProvider provider, [NotNullWhen(true)] out string? id) { return instance.TryGetProviderId(provider.ToString(), out id); } From f666b7e10268a5c19228d8fdb1bd313a642b0915 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 27 Feb 2021 20:12:55 +0000 Subject: [PATCH 015/402] fix --- .../ApplicationHost.cs | 22 ++++++++++++++----- .../IStartupOptions.cs | 12 +++++----- Jellyfin.Server/CoreAppHost.cs | 4 ++++ Jellyfin.Server/Program.cs | 1 + Jellyfin.Server/StartupOptions.cs | 4 ++-- .../JellyfinApplicationFactory.cs | 2 ++ 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 1b9bb86bb..785a3e89c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -99,6 +99,7 @@ using MediaBrowser.Providers.Subtitles; using MediaBrowser.XbmcMetadata.Providers; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; @@ -118,6 +119,7 @@ namespace Emby.Server.Implementations private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" }; private readonly IFileSystem _fileSystemManager; + private readonly IConfiguration _startupConfig; private readonly IXmlSerializer _xmlSerializer; private readonly IStartupOptions _startupOptions; private readonly IPluginManager _pluginManager; @@ -228,6 +230,11 @@ namespace Emby.Server.Implementations /// public int HttpsPort { get; private set; } + /// + /// Gets the PublishedServerUrl setting. + /// + public string PublishedServerUrl => _startupOptions.PublishedServerUrl ?? _startupConfig["PublishedServerUrl"]; + /// /// Gets the server configuration manager. /// @@ -240,12 +247,14 @@ namespace Emby.Server.Implementations /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// The interface. /// Instance of the interface. /// Instance of the interface. public ApplicationHost( IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, + IConfiguration startupConfig, IFileSystem fileSystem, IServiceCollection serviceCollection) { @@ -268,6 +277,7 @@ namespace Emby.Server.Implementations Logger = LoggerFactory.CreateLogger(); _startupOptions = options; + _startupConfig = startupConfig; // Initialize runtime stat collection if (ServerConfigurationManager.Configuration.EnableMetrics) @@ -1145,10 +1155,10 @@ namespace Emby.Server.Implementations public string GetSmartApiUrl(IPAddress ipAddress, int? port = null) { // Published server ends with a / - if (_startupOptions.PublishedServerUrl != null) + if (!string.IsNullOrEmpty(PublishedServerUrl)) { // Published server ends with a '/', so we need to remove it. - return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + return PublishedServerUrl.Trim('/'); } string smart = NetManager.GetBindInterface(ipAddress, out port); @@ -1165,10 +1175,10 @@ namespace Emby.Server.Implementations public string GetSmartApiUrl(HttpRequest request, int? port = null) { // Published server ends with a / - if (_startupOptions.PublishedServerUrl != null) + if (!string.IsNullOrEmpty(PublishedServerUrl)) { // Published server ends with a '/', so we need to remove it. - return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + return PublishedServerUrl.Trim('/'); } string smart = NetManager.GetBindInterface(request, out port); @@ -1185,10 +1195,10 @@ namespace Emby.Server.Implementations public string GetSmartApiUrl(string hostname, int? port = null) { // Published server ends with a / - if (_startupOptions.PublishedServerUrl != null) + if (!string.IsNullOrEmpty(PublishedServerUrl)) { // Published server ends with a '/', so we need to remove it. - return _startupOptions.PublishedServerUrl.ToString().Trim('/'); + return PublishedServerUrl.Trim('/'); } string smart = NetManager.GetBindInterface(hostname, out port); diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs index 4bef59543..0b823ff06 100644 --- a/Emby.Server.Implementations/IStartupOptions.cs +++ b/Emby.Server.Implementations/IStartupOptions.cs @@ -1,5 +1,5 @@ #pragma warning disable CS1591 - +#nullable enable using System; namespace Emby.Server.Implementations @@ -9,7 +9,7 @@ namespace Emby.Server.Implementations /// /// Gets the value of the --ffmpeg command line option. /// - string FFmpegPath { get; } + string? FFmpegPath { get; } /// /// Gets the value of the --service command line option. @@ -19,21 +19,21 @@ namespace Emby.Server.Implementations /// /// Gets the value of the --package-name command line option. /// - string PackageName { get; } + string? PackageName { get; } /// /// Gets the value of the --restartpath command line option. /// - string RestartPath { get; } + string? RestartPath { get; } /// /// Gets the value of the --restartargs command line option. /// - string RestartArgs { get; } + string? RestartArgs { get; } /// /// Gets the value of the --published-server-url command line option. /// - Uri PublishedServerUrl { get; } + string? PublishedServerUrl { get; } } } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index b76aa5e14..1daa32dee 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -21,6 +21,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; using MediaBrowser.Model.IO; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -37,18 +38,21 @@ namespace Jellyfin.Server /// The to be used by the . /// The to be used by the . /// The to be used by the . + /// The to be used by the . /// The to be used by the . /// The to be used by the . public CoreAppHost( IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, + IConfiguration startupConfig, IFileSystem fileSystem, IServiceCollection collection) : base( applicationPaths, loggerFactory, options, + startupConfig, fileSystem, collection) { diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index f05cdfe9b..18aa91ee1 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -164,6 +164,7 @@ namespace Jellyfin.Server appPaths, _loggerFactory, options, + startupConfig, new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), serviceCollection); diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index b63434092..6d8210527 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -77,7 +77,7 @@ namespace Jellyfin.Server /// [Option("published-server-url", Required = false, HelpText = "Jellyfin Server URL to publish via auto discover process")] - public Uri? PublishedServerUrl { get; set; } + public string? PublishedServerUrl { get; set; } /// /// Gets the command line options as a dictionary that can be used in the .NET configuration system. @@ -94,7 +94,7 @@ namespace Jellyfin.Server if (PublishedServerUrl != null) { - config.Add(UdpServer.AddressOverrideConfigKey, PublishedServerUrl.ToString()); + config.Add(UdpServer.AddressOverrideConfigKey, PublishedServerUrl); } if (FFmpegPath != null) diff --git a/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs index 54f8eb225..262e8f912 100644 --- a/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs @@ -8,6 +8,7 @@ using MediaBrowser.Common; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog; @@ -77,6 +78,7 @@ namespace Jellyfin.Api.Tests appPaths, loggerFactory, commandLineOpts, + new ConfigurationBuilder().Build(), new ManagedFileSystem(loggerFactory.CreateLogger(), appPaths), serviceCollection); _disposableComponents.Add(appHost); From 1d6f489f17919e557987b2616048dc8def790f43 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 28 Feb 2021 10:11:37 +0000 Subject: [PATCH 016/402] comment change --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 785a3e89c..7740b4fc5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -231,7 +231,7 @@ namespace Emby.Server.Implementations public int HttpsPort { get; private set; } /// - /// Gets the PublishedServerUrl setting. + /// Gets the value of the PublishedServerUrl setting. /// public string PublishedServerUrl => _startupOptions.PublishedServerUrl ?? _startupConfig["PublishedServerUrl"]; From 159ecb882f419e485a95e727b647f0e2cf6eeb3d Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 28 Feb 2021 10:14:05 +0000 Subject: [PATCH 017/402] Fixed bad sync --- Emby.Server.Implementations/ApplicationHost.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6880b1842..37047a4f7 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -137,9 +137,6 @@ namespace Emby.Server.Implementations public bool CoreStartupHasCompleted { get; private set; } - /// - public Uri PublishedServerUrl => _startupOptions.PublishedServerUrl; - public virtual bool CanLaunchWebBrowser { get From caa8e7cdf37a426b983792077542ef0bb50721a7 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 28 Feb 2021 10:16:28 +0000 Subject: [PATCH 018/402] fixed build --- Emby.Dlna/Main/DlnaEntryPoint.cs | 2 +- MediaBrowser.Controller/IServerApplicationHost.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index ec8716029..9a2d524d1 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -316,7 +316,7 @@ namespace Emby.Dlna.Main _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address); var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri); - if (_appHost.PublishedServerUrl == null) + if (!string.IsNullOrEmpty(_appHost.PublishedServerUrl)) { // DLNA will only work over http, so we must reset to http:// : {port}. uri.Scheme = "http"; diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 6378625e8..20bfa697e 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -55,7 +55,7 @@ namespace MediaBrowser.Controller /// /// Gets the configured published server url. /// - Uri PublishedServerUrl { get; } + string PublishedServerUrl { get; } /// /// Gets the system info. From 8836242559710cbe4577451c65f91133c3da4a79 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 28 Feb 2021 10:25:14 +0000 Subject: [PATCH 019/402] fixed tests --- tests/Jellyfin.Api.Tests/TestAppHost.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Jellyfin.Api.Tests/TestAppHost.cs b/tests/Jellyfin.Api.Tests/TestAppHost.cs index 772e98d04..eb4c9b305 100644 --- a/tests/Jellyfin.Api.Tests/TestAppHost.cs +++ b/tests/Jellyfin.Api.Tests/TestAppHost.cs @@ -4,6 +4,7 @@ using Emby.Server.Implementations; using Jellyfin.Server; using MediaBrowser.Controller; using MediaBrowser.Model.IO; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -20,18 +21,21 @@ namespace Jellyfin.Api.Tests /// The to be used by the . /// The to be used by the . /// The to be used by the . + /// The to be used by the . /// The to be used by the . /// The to be used by the . public TestAppHost( IServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IStartupOptions options, + IConfiguration startup, IFileSystem fileSystem, IServiceCollection collection) : base( applicationPaths, loggerFactory, options, + startup, fileSystem, collection) { From 16694b0cfcc51ba009a0aedce0d383ab010e8b99 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 27 Feb 2021 22:46:03 +0100 Subject: [PATCH 020/402] Add nfo thumb tag support --- .../ApplicationHost.cs | 2 + .../Providers/MetadataResult.cs | 12 ++- .../Manager/MetadataService.cs | 6 ++ .../Parsers/BaseNfoParser.cs | 78 ++++++++++++++++++- .../Parsers/EpisodeNfoParser.cs | 6 +- .../Parsers/MovieNfoParser.cs | 6 +- .../Parsers/SeasonNfoParser.cs | 6 +- .../Parsers/SeriesNfoParser.cs | 6 +- .../Providers/AlbumNfoProvider.cs | 8 +- .../Providers/ArtistNfoProvider.cs | 8 +- .../Providers/BaseVideoNfoProvider.cs | 9 ++- .../Providers/EpisodeNfoProvider.cs | 8 +- .../Providers/MovieNfoProvider.cs | 6 +- .../Providers/MusicVideoNfoProvider.cs | 6 +- .../Providers/SeasonNfoProvider.cs | 8 +- .../Providers/SeriesNfoProvider.cs | 8 +- .../Providers/VideoNfoProvider.cs | 6 +- .../Parsers/EpisodeNfoProviderTests.cs | 9 ++- .../Parsers/MovieNfoParserTests.cs | 64 ++++++++++++++- .../Parsers/MusicAlbumNfoProviderTests.cs | 11 ++- .../Parsers/MusicArtistNfoParserTests.cs | 9 ++- .../Parsers/MusicVideoNfoParserTests.cs | 9 ++- .../Parsers/SeasonNfoProviderTests.cs | 9 ++- .../Parsers/SeriesNfoParserTests.cs | 3 +- .../Test Data/Justice League.nfo | 2 + 25 files changed, 265 insertions(+), 40 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 1b9bb86bb..788e08e48 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -682,6 +682,8 @@ namespace Emby.Server.Implementations ServiceCollection.AddScoped(); ServiceCollection.AddScoped(); ServiceCollection.AddScoped(); + + ServiceCollection.AddSingleton(); } /// diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index 1c695cafa..864cb3050 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -4,21 +4,25 @@ using System; using System.Collections.Generic; using System.Globalization; using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Providers { public class MetadataResult { - public List Images { get; set; } - - public List UserDataList { get; set; } - public MetadataResult() { Images = new List(); + RemoteImages = new List<(string url, ImageType type)>(); ResultLanguage = "en"; } + public List Images { get; set; } + + public List<(string url, ImageType type)> RemoteImages { get; set; } + + public List UserDataList { get; set; } + public List People { get; set; } public bool HasMetadata { get; set; } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 8b3ca17ca..494e479a5 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -706,6 +706,12 @@ namespace MediaBrowser.Providers.Manager if (localItem.HasMetadata) { + foreach (var remoteImage in localItem.RemoteImages) + { + await ProviderManager.SaveImage(item, remoteImage.url, remoteImage.type, null, cancellationToken).ConfigureAwait(false); + refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate; + } + if (imageService.MergeImages(item, localItem.Images)) { refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate; diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 6f164caf3..aa89936d0 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -27,6 +27,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers private readonly IConfigurationManager _config; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IDirectoryService _directoryService; private Dictionary _validProviderIds; /// @@ -37,12 +38,14 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public BaseNfoParser( ILogger logger, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) { Logger = logger; _config = config; @@ -50,6 +53,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers _validProviderIds = new Dictionary(); _userManager = userManager; _userDataManager = userDataManager; + _directoryService = directoryService; } protected CultureInfo UsCulture { get; } = new CultureInfo("en-US"); @@ -785,6 +789,78 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } + case "thumb": + { + var artType = reader.GetAttribute("aspect"); + var val = reader.ReadElementContentAsString(); + + // skip: + // - empty aspect tag + // - empty uri + // - tag containing '.' because we can't set images for seasons, episodes or movie sets within series or movies + if (string.IsNullOrEmpty(artType) || string.IsNullOrEmpty(val) || artType.Contains('.', StringComparison.Ordinal)) + { + break; + } + + ImageType imageType = artType switch + { + "banner" => ImageType.Banner, + "clearlogo" => ImageType.Logo, + "discart" => ImageType.Disc, + "landscape" => ImageType.Thumb, + "clearart" => ImageType.Art, + // unknown type (including "poster") --> primary + _ => ImageType.Primary, + }; + + Uri uri; + try + { + uri = new Uri(val); + } + catch (UriFormatException ex) + { + Logger.LogError(ex, "Image location {Path} specified in nfo file for {ItemName} is not a valid URL or file path.", val, item.Name); + break; + } + + if (uri.IsFile) + { + // only allow one item of each type + if (itemResult.Images.Any(x => x.Type == imageType)) + { + break; + } + + var fileSystemMetadata = _directoryService.GetFile(val); + // non existing file returns null + if (fileSystemMetadata == null || !fileSystemMetadata.Exists) + { + Logger.LogWarning("Artwork file {Path} specified in nfo file for {ItemName} does not exist.", uri, item.Name); + break; + } + + itemResult.Images.Add(new LocalImageInfo() + { + FileInfo = fileSystemMetadata, + Type = imageType + }); + } + else + { + // only allow one item of each type + if (itemResult.RemoteImages.Any(x => x.type == imageType)) + { + break; + } + + itemResult.RemoteImages.Add((uri.ToString(), imageType)); + } + + break; + } + default: string readerName = reader.Name; if (_validProviderIds.TryGetValue(readerName, out string? providerIdValue)) diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index f0c50d8e5..7ae55c791 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -25,13 +25,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public EpisodeNfoParser( ILogger logger, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) - : base(logger, config, providerManager, userManager, userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) + : base(logger, config, providerManager, userManager, userDataManager, directoryService) { } diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs index 2d0eb8433..b466fa25a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs @@ -25,13 +25,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public MovieNfoParser( ILogger logger, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) - : base(logger, config, providerManager, userManager, userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) + : base(logger, config, providerManager, userManager, userDataManager, directoryService) { } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs index bd2607bd8..2f5fd40e2 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs @@ -21,13 +21,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public SeasonNfoParser( ILogger logger, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) - : base(logger, config, providerManager, userManager, userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) + : base(logger, config, providerManager, userManager, userDataManager, directoryService) { } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index fbab8b521..f6574c365 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -22,13 +22,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public SeriesNfoParser( ILogger logger, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) - : base(logger, config, providerManager, userManager, userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) + : base(logger, config, providerManager, userManager, userDataManager, directoryService) { } diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs index 24f127411..bd557d783 100644 --- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IProviderManager _providerManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IDirectoryService _directoryService; /// /// Initializes a new instance of the class. @@ -30,13 +31,15 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public AlbumNfoProvider( ILogger logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) : base(fileSystem) { _logger = logger; @@ -44,12 +47,13 @@ namespace MediaBrowser.XbmcMetadata.Providers _providerManager = providerManager; _userManager = userManager; _userDataManager = userDataManager; + _directoryService = directoryService; } /// protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new BaseNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken); + new BaseNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager, _directoryService).Fetch(result, path, cancellationToken); } /// diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs index fac28ab59..54bb83114 100644 --- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IProviderManager _providerManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IDirectoryService _directoryService; /// /// Initializes a new instance of the class. @@ -30,13 +31,15 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public ArtistNfoProvider( IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) : base(fileSystem) { _logger = logger; @@ -44,12 +47,13 @@ namespace MediaBrowser.XbmcMetadata.Providers _providerManager = providerManager; _userManager = userManager; _userDataManager = userDataManager; + _directoryService = directoryService; } /// protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new BaseNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken); + new BaseNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager, _directoryService).Fetch(result, path, cancellationToken); } /// diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs index af722748b..b8f76f31d 100644 --- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs @@ -21,6 +21,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IProviderManager _providerManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IDirectoryService _directoryService; public BaseVideoNfoProvider( ILogger> logger, @@ -28,7 +29,8 @@ namespace MediaBrowser.XbmcMetadata.Providers IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) : base(fileSystem) { _logger = logger; @@ -36,6 +38,7 @@ namespace MediaBrowser.XbmcMetadata.Providers _providerManager = providerManager; _userManager = userManager; _userDataManager = userDataManager; + _directoryService = directoryService; } /// @@ -45,10 +48,12 @@ namespace MediaBrowser.XbmcMetadata.Providers { Item = result.Item }; - new MovieNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(tmpItem, path, cancellationToken); + new MovieNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager, _directoryService).Fetch(tmpItem, path, cancellationToken); result.Item = (T)tmpItem.Item; result.People = tmpItem.People; + result.Images = tmpItem.Images; + result.RemoteImages = tmpItem.RemoteImages; if (tmpItem.UserDataList != null) { diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs index 7233f99dc..64b208345 100644 --- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IProviderManager _providerManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IDirectoryService _directoryService; /// /// Initializes a new instance of the class. @@ -30,13 +31,15 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public EpisodeNfoProvider( ILogger logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) : base(fileSystem) { _logger = logger; @@ -44,12 +47,13 @@ namespace MediaBrowser.XbmcMetadata.Providers _providerManager = providerManager; _userManager = userManager; _userDataManager = userDataManager; + _directoryService = directoryService; } /// protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new EpisodeNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken); + new EpisodeNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager, _directoryService).Fetch(result, path, cancellationToken); } /// diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs index 811d39a9d..cdbc5a918 100644 --- a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs @@ -21,14 +21,16 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public MovieNfoProvider( ILogger logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) - : base(logger, fileSystem, config, providerManager, userManager, userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) + : base(logger, fileSystem, config, providerManager, userManager, userDataManager, directoryService) { } } diff --git a/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs index 09df509ee..9d1f3e61d 100644 --- a/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/MusicVideoNfoProvider.cs @@ -21,14 +21,16 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public MusicVideoNfoProvider( ILogger logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) - : base(logger, fileSystem, config, providerManager, userManager, userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) + : base(logger, fileSystem, config, providerManager, userManager, userDataManager, directoryService) { } } diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs index 8f0ed6df7..97220cf7e 100644 --- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IProviderManager _providerManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IDirectoryService _directoryService; /// /// Initializes a new instance of the class. @@ -30,13 +31,15 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public SeasonNfoProvider( ILogger logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) : base(fileSystem) { _logger = logger; @@ -44,12 +47,13 @@ namespace MediaBrowser.XbmcMetadata.Providers _providerManager = providerManager; _userManager = userManager; _userDataManager = userDataManager; + _directoryService = directoryService; } /// protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new SeasonNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken); + new SeasonNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager, _directoryService).Fetch(result, path, cancellationToken); } /// diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs index 3e496dc58..9a9b94123 100644 --- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IProviderManager _providerManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IDirectoryService _directoryService; /// /// Initializes a new instance of the class. @@ -30,13 +31,15 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public SeriesNfoProvider( ILogger logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) : base(fileSystem) { _logger = logger; @@ -44,12 +47,13 @@ namespace MediaBrowser.XbmcMetadata.Providers _providerManager = providerManager; _userManager = userManager; _userDataManager = userDataManager; + _directoryService = directoryService; } /// protected override void Fetch(MetadataResult result, string path, CancellationToken cancellationToken) { - new SeriesNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager).Fetch(result, path, cancellationToken); + new SeriesNfoParser(_logger, _config, _providerManager, _userManager, _userDataManager, _directoryService).Fetch(result, path, cancellationToken); } /// diff --git a/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs index 4717d81e6..93b1be62f 100644 --- a/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/VideoNfoProvider.cs @@ -21,14 +21,16 @@ namespace MediaBrowser.XbmcMetadata.Providers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public VideoNfoProvider( ILogger logger, IFileSystem fileSystem, IConfigurationManager config, IProviderManager providerManager, IUserManager userManager, - IUserDataManager userDataManager) - : base(logger, fileSystem, config, providerManager, userManager, userDataManager) + IUserDataManager userDataManager, + IDirectoryService directoryService) + : base(logger, fileSystem, config, providerManager, userManager, userDataManager, directoryService) { } } diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs index d10ef9b47..438684473 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs @@ -37,8 +37,15 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers .Returns(new XbmcMetadataOptions()); var user = new Mock(); var userData = new Mock(); + var directoryService = new Mock(); - _parser = new EpisodeNfoParser(new NullLogger(), config.Object, providerManager.Object, user.Object, userData.Object); + _parser = new EpisodeNfoParser( + new NullLogger(), + config.Object, + providerManager.Object, + user.Object, + userData.Object, + directoryService.Object); } [Fact] diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 76231391e..76e10b451 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -9,7 +9,9 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; +using MediaBrowser.Model.System; using MediaBrowser.Providers.Plugins.Tmdb.Movies; using MediaBrowser.XbmcMetadata.Parsers; using Microsoft.Extensions.Logging.Abstractions; @@ -23,6 +25,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers private readonly MovieNfoParser _parser; private readonly IUserDataManager _userDataManager; private readonly User _testUser; + private readonly FileSystemMetadata _localImageFileMetadata; public MovieNfoParserTests() { @@ -52,8 +55,36 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers userData.Setup(x => x.GetUserData(_testUser, It.IsAny())) .Returns(new UserItemData()); + var directoryService = new Mock(); + if (MediaBrowser.Common.System.OperatingSystem.Id != OperatingSystemId.Windows) + { + _localImageFileMetadata = new FileSystemMetadata() + { + Exists = true, + FullName = "/media/movies/Justice League (2017).jpg" + }; + directoryService.Setup(x => x.GetFile(_localImageFileMetadata.FullName)) + .Returns(_localImageFileMetadata); + } + else + { + _localImageFileMetadata = new FileSystemMetadata() + { + Exists = true, + FullName = "C:\\media\\movies\\Justice League (2017).jpg" + }; + directoryService.Setup(x => x.GetFile(_localImageFileMetadata.FullName)) + .Returns(_localImageFileMetadata); + } + _userDataManager = userData.Object; - _parser = new MovieNfoParser(new NullLogger(), configManager.Object, providerManager.Object, user.Object, userData.Object); + _parser = new MovieNfoParser( + new NullLogger(), + configManager.Object, + providerManager.Object, + user.Object, + userData.Object, + directoryService.Object); } [Fact] @@ -134,6 +165,37 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers // Movie set Assert.Equal("702342", item.ProviderIds[MetadataProvider.TmdbCollection.ToString()]); Assert.Equal("Justice League Collection", item.CollectionName); + + // Images + Assert.Equal(6, result.RemoteImages.Count); + + var posters = result.RemoteImages.Where(x => x.type == ImageType.Primary).ToList(); + Assert.Single(posters); + Assert.Equal("http://image.tmdb.org/t/p/original/9rtrRGeRnL0JKtu9IMBWsmlmmZz.jpg", posters[0].url); + + var logos = result.RemoteImages.Where(x => x.type == ImageType.Logo).ToList(); + Assert.Single(logos); + Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png", logos[0].url); + + var banners = result.RemoteImages.Where(x => x.type == ImageType.Banner).ToList(); + Assert.Single(banners); + Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-586017e95adbd.jpg", banners[0].url); + + var thumbs = result.RemoteImages.Where(x => x.type == ImageType.Thumb).ToList(); + Assert.Single(thumbs); + Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585fb155c3743.jpg", thumbs[0].url); + + var art = result.RemoteImages.Where(x => x.type == ImageType.Art).ToList(); + Assert.Single(art); + Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/hdmovieclearart/justice-league-5865c23193041.png", art[0].url); + + var discArt = result.RemoteImages.Where(x => x.type == ImageType.Disc).ToList(); + Assert.Single(discArt); + Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png", discArt[0].url); + + // Local Image - contains only one item depending on operating system + Assert.Single(result.Images); + Assert.Equal(_localImageFileMetadata.Name, result.Images[0].FileInfo.Name); } [Theory] diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs index 2183d2a2f..29de2a95e 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs @@ -1,7 +1,6 @@ #pragma warning disable CA5369 using System; -using System.Linq; using System.Threading; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.Audio; @@ -11,7 +10,6 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Providers.Music; -using MediaBrowser.Providers.Plugins.MusicBrainz; using MediaBrowser.XbmcMetadata.Parsers; using Microsoft.Extensions.Logging.Abstractions; using Moq; @@ -38,8 +36,15 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers .Returns(new XbmcMetadataOptions()); var user = new Mock(); var userData = new Mock(); + var directoryService = new Mock(); - _parser = new BaseNfoParser(new NullLogger>(), config.Object, providerManager.Object, user.Object, userData.Object); + _parser = new BaseNfoParser( + new NullLogger>(), + config.Object, + providerManager.Object, + user.Object, + userData.Object, + directoryService.Object); } [Fact] diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs index f86b7604e..e54b35eb5 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs @@ -35,8 +35,15 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers .Returns(new XbmcMetadataOptions()); var user = new Mock(); var userData = new Mock(); + var directoryService = new Mock(); - _parser = new BaseNfoParser(new NullLogger>(), config.Object, providerManager.Object, user.Object, userData.Object); + _parser = new BaseNfoParser( + new NullLogger>(), + config.Object, + providerManager.Object, + user.Object, + userData.Object, + directoryService.Object); } [Fact] diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs index 898554936..bf887cab1 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicVideoNfoParserTests.cs @@ -30,8 +30,15 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers var user = new Mock(); var userData = new Mock(); + var directoryService = new Mock(); - _parser = new MovieNfoParser(new NullLogger>(), config.Object, providerManager.Object, user.Object, userData.Object); + _parser = new MovieNfoParser( + new NullLogger>(), + config.Object, + providerManager.Object, + user.Object, + userData.Object, + directoryService.Object); } [Fact] diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs index 602db7c09..c497d4fa2 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs @@ -31,8 +31,15 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers .Returns(new XbmcMetadataOptions()); var user = new Mock(); var userData = new Mock(); + var directoryService = new Mock(); - _parser = new SeasonNfoParser(new NullLogger(), config.Object, providerManager.Object, user.Object, userData.Object); + _parser = new SeasonNfoParser( + new NullLogger(), + config.Object, + providerManager.Object, + user.Object, + userData.Object, + directoryService.Object); } [Fact] diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs index f8eb04b3a..7c7d698f0 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs @@ -29,8 +29,9 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers .Returns(new XbmcMetadataOptions()); var user = new Mock(); var userData = new Mock(); + var directoryService = new Mock(); - _parser = new SeriesNfoParser(new NullLogger(), config.Object, providerManager.Object, user.Object, userData.Object); + _parser = new SeriesNfoParser(new NullLogger(), config.Object, providerManager.Object, user.Object, userData.Object, directoryService.Object); } [Fact] diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo index 72e27fe50..c35d37b18 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo +++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo @@ -59,6 +59,8 @@ http://image.tmdb.org/t/p/original/exLtrlI7JjKcfQVTccI7XdQRFMz.jpg http://image.tmdb.org/t/p/original/paLcue01KpfQftorfjKqqD4qvlL.jpg http://image.tmdb.org/t/p/original/yVDIfiKIsCbdFcgLXW34bAsnQvy.jpg + C:\media\movies\Justice League (2017).jpg + /media/movies/Justice League (2017).jpg https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-585e9ca3bcf6a.png https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-57b476a831d74.png From 4bbfcaef83579239a9bef708c71a889cb3d829b4 Mon Sep 17 00:00:00 2001 From: Moshe Schmidt Date: Wed, 17 Feb 2021 21:18:27 +0100 Subject: [PATCH 021/402] Include specials in the calculation for the "Next Up" episode. Fixes #1479 --- CONTRIBUTORS.md | 1 + .../Sorting/AiredEpisodeOrderComparer.cs | 4 +- .../TV/TVSeriesManager.cs | 46 +++++++++++++++---- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1200275d5..bcc27b4dd 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -104,6 +104,7 @@ - [shemanaev](https://github.com/shemanaev) - [skaro13](https://github.com/skaro13) - [sl1288](https://github.com/sl1288) + - [Smith00101010](https://github.com/Smith00101010) - [sorinyo2004](https://github.com/sorinyo2004) - [sparky8251](https://github.com/sparky8251) - [spookbits](https://github.com/spookbits) diff --git a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs index 1f68a9c81..60698e803 100644 --- a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs +++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs @@ -131,11 +131,11 @@ namespace Emby.Server.Implementations.Sorting return GetSpecialCompareValue(x).CompareTo(GetSpecialCompareValue(y)); } - private static int GetSpecialCompareValue(Episode item) + private static long GetSpecialCompareValue(Episode item) { // First sort by season number // Since there are three sort orders, pad with 9 digits (3 for each, figure 1000 episode buffer should be enough) - var val = (item.AirsAfterSeasonNumber ?? item.AirsBeforeSeasonNumber ?? 0) * 1000000000; + var val = (item.AirsAfterSeasonNumber ?? item.AirsBeforeSeasonNumber ?? 0) * 1000000000L; // Second sort order is if it airs after the season if (item.AirsAfterSeasonNumber.HasValue) diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 839b62448..d111e1a1c 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; @@ -11,7 +10,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.TV; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using Episode = MediaBrowser.Controller.Entities.TV.Episode; using Series = MediaBrowser.Controller.Entities.TV.Series; @@ -23,12 +21,14 @@ namespace Emby.Server.Implementations.TV private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _configurationManager; - public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager) + public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager) { _userManager = userManager; _userDataManager = userDataManager; _libraryManager = libraryManager; + _configurationManager = configurationManager; } public QueryResult GetNextUp(NextUpQuery request, DtoOptions dtoOptions) @@ -200,13 +200,10 @@ namespace Emby.Server.Implementations.TV ParentIndexNumberNotEquals = 0, DtoOptions = new DtoOptions { - Fields = new ItemFields[] - { - ItemFields.SortName - }, + Fields = new[] { ItemFields.SortName }, EnableImages = false } - }).FirstOrDefault(); + }).Cast().FirstOrDefault(); Func getEpisode = () => { @@ -224,6 +221,39 @@ namespace Emby.Server.Implementations.TV DtoOptions = dtoOptions }).Cast().FirstOrDefault(); + if (_configurationManager.Configuration.DisplaySpecialsWithinSeasons) + { + var consideredEpisodes = _libraryManager.GetItemList(new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = null, + SeriesPresentationUniqueKey = seriesKey, + ParentIndexNumber = 0, + IncludeItemTypes = new[] { nameof(Episode) }, + IsPlayed = false, + IsVirtualItem = false, + DtoOptions = dtoOptions + }).Cast().Where(episode => episode.AirsBeforeSeasonNumber != null || episode.AirsAfterSeasonNumber != null).ToList(); + + if (lastWatchedEpisode != null) + { + // Last watched episode is added, because there could be specials that aired before the last watched episode + consideredEpisodes.Add(lastWatchedEpisode); + } + + if (nextEpisode != null) + { + consideredEpisodes.Add(nextEpisode); + } + + var sortedConsideredEpisodes = _libraryManager.Sort(consideredEpisodes, user, new[] { (ItemSortBy.AiredEpisodeOrder, SortOrder.Ascending) }).Cast(); + if (lastWatchedEpisode != null) + { + sortedConsideredEpisodes = sortedConsideredEpisodes.SkipWhile(episode => episode.Id != lastWatchedEpisode.Id).Skip(1); + } + + nextEpisode = sortedConsideredEpisodes.FirstOrDefault(); + } + if (nextEpisode != null) { var userData = _userDataManager.GetUserData(user, nextEpisode); From 18cd634ec8316fe5978d7ad3867de66b85cd5fba Mon Sep 17 00:00:00 2001 From: Mister Rajoy Date: Sun, 28 Feb 2021 23:03:28 +0100 Subject: [PATCH 022/402] Fix duplicated movies when group movies into collections is enabled --- .../Collections/CollectionManager.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 1ab2bdfbe..449f8872f 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -344,7 +344,20 @@ namespace Emby.Server.Implementations.Collections } else { - results[item.Id] = item; + var alreadyInResults = false; + foreach (var child in item.GetMediaSources(true)) + { + + if (results.ContainsKey(Guid.Parse(child.Id))) + { + alreadyInResults = true; + } + } + if (!alreadyInResults) + { + + results[item.Id] = item; + } } } } From ed0267252f1a2b5922db7a52ad63799a5009a6ce Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Mar 2021 20:00:00 +0100 Subject: [PATCH 023/402] Remove tests that are upstreamed libse (the SSA parser we use) has these same tests now --- .../Subtitles/SsaParserTests.cs | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs index 5033d1de9..5db80c300 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs @@ -13,38 +13,11 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { public class SsaParserTests { - // commonly shared invariant value between tests, assumes default format order - private const string InvariantDialoguePrefix = "[Events]\nDialogue: ,0:00:00.00,0:00:00.01,,,,,,,"; - private readonly SsaParser _parser = new SsaParser(new NullLogger()); - [Theory] - [InlineData("[EvEnTs]\nDialogue: ,0:00:00.00,0:00:00.01,,,,,,,text", "text")] // label casing insensitivity - [InlineData("[Events]\n,0:00:00.00,0:00:00.01,,,,,,,labelless dialogue", "labelless dialogue")] // no "Dialogue:" label, it is optional - // TODO: Fix upstream - // [InlineData("[Events]\nFormat: Text, Start, End, Layer, Effect, Style\nDialogue: reordered text,0:00:00.00,0:00:00.01", "reordered text")] // reordered formats - [InlineData(InvariantDialoguePrefix + "Cased TEXT", "Cased TEXT")] // preserve text casing - [InlineData(InvariantDialoguePrefix + " text ", " text ")] // do not trim text - [InlineData(InvariantDialoguePrefix + "text, more text", "text, more text")] // append excess dialogue values (> 10) to text - [InlineData(InvariantDialoguePrefix + "start {\\fnFont Name}text{\\fn} end", "start text end")] // font name - [InlineData(InvariantDialoguePrefix + "start {\\fs10}text{\\fs} end", "start text end")] // font size - [InlineData(InvariantDialoguePrefix + "start {\\c&H112233}text{\\c} end", "start text end")] // color - // TODO: Fix upstream - // [InlineData(InvariantDialoguePrefix + "start {\\1c&H112233}text{\\1c} end", "start text end")] // primay color - // [InlineData(InvariantDialoguePrefix + "start {\\fnFont Name}text1 {\\fs10}text2{\\fs}{\\fn} {\\1c&H112233}text3{\\1c} end", "start text1 text2 text3 end")] // nested formatting - public void Parse(string ssa, string expectedText) - { - using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa))) - { - SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, CancellationToken.None); - SubtitleTrackEvent actual = subtitleTrackInfo.TrackEvents[0]; - Assert.Equal(expectedText, actual.Text); - } - } - [Theory] [MemberData(nameof(Parse_MultipleDialogues_TestData))] - public void Parse_MultipleDialogues(string ssa, IReadOnlyList expectedSubtitleTrackEvents) + public void Parse_MultipleDialogues_Success(string ssa, IReadOnlyList expectedSubtitleTrackEvents) { using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa))) { From ba62d9d1fe84dfb16c502ab7e105c6c6807770ab Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Mar 2021 20:35:38 +0100 Subject: [PATCH 024/402] Revert breaking change --- .../Entities/ProviderIdsExtensions.cs | 27 +++++++++++++ .../Entities/ProviderIdsExtensionsTests.cs | 38 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 11c3dbe42..bde5a1da1 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -9,6 +9,33 @@ namespace MediaBrowser.Model.Entities /// public static class ProviderIdsExtensions { + /// + /// Checks if this instance has an id for the given provider. + /// + /// The instance. + /// The of the provider name. + /// true if a provider id with the given name was found; otherwise false. + public static bool HasProviderId(this IHasProviderIds instance, string name) + { + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + return instance.ProviderIds?.ContainsKey(name) ?? false; + } + + /// + /// Checks if this instance has an id for the given provider. + /// + /// The instance. + /// The provider. + /// true if a provider id with the given name was found; otherwise false. + public static bool HasProviderId(this IHasProviderIds instance, MetadataProvider provider) + { + return instance.HasProviderId(provider.ToString()); + } + /// /// Gets a provider id. /// diff --git a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs index c1a1525ba..2b2414ef1 100644 --- a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs @@ -9,6 +9,44 @@ namespace Jellyfin.Model.Tests.Entities { private const string ExampleImdbId = "tt0113375"; + [Fact] + public void HasProviderId_NullInstance_ThrowsArgumentNullException() + { + Assert.Throws(() => ProviderIdsExtensions.HasProviderId(null!, MetadataProvider.Imdb)); + } + + [Fact] + public void HasProviderId_NullProvider_False() + { + var nullProvider = new ProviderIdsExtensionsTestsObject() + { + ProviderIds = null! + }; + + Assert.False(nullProvider.HasProviderId(MetadataProvider.Imdb)); + } + + [Fact] + public void HasProviderId_NullName_ThrowsArgumentNullException() + { + Assert.Throws(() => ProviderIdsExtensionsTestsObject.Empty.HasProviderId(null!)); + } + + [Fact] + public void HasProviderId_NotFoundName_False() + { + Assert.False(ProviderIdsExtensionsTestsObject.Empty.HasProviderId(MetadataProvider.Imdb)); + } + + [Fact] + public void HasProviderId_FoundName_True() + { + var provider = new ProviderIdsExtensionsTestsObject(); + provider.ProviderIds[MetadataProvider.Imdb.ToString()] = ExampleImdbId; + + Assert.True(provider.HasProviderId(MetadataProvider.Imdb)); + } + [Fact] public void GetProviderId_NullInstance_ThrowsArgumentNullException() { From fdd4b6b3f146c4598fb1709f71dd11ffa623db28 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 1 Mar 2021 21:02:20 +0000 Subject: [PATCH 025/402] Changed message --- Jellyfin.Server/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index f05cdfe9b..461ff163c 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -198,7 +198,7 @@ namespace Jellyfin.Server } catch { - _logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in system.xml and try again."); + _logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in network.xml and try again."); throw; } From dedc94ec91d3562a71cc9d7e88c8c23f373d64a2 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 1 Mar 2021 21:32:49 +0000 Subject: [PATCH 026/402] correction of ip6 loopback --- Jellyfin.Networking/Manager/NetworkManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 51fcb6d9a..9c7d096a7 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -414,7 +414,7 @@ namespace Jellyfin.Networking.Manager } // There isn't any others, so we'll use the loopback. - result = IsIP6Enabled ? "::" : "127.0.0.1"; + result = IsIP6Enabled ? "::1" : "127.0.0.1"; _logger.LogWarning("{Source}: GetBindInterface: Loopback {Result} returned.", source, result); return result; } From 594294871438bd83fee3198c647b2ff30defa8e1 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 1 Mar 2021 23:42:04 +0000 Subject: [PATCH 027/402] Kestrel workaround --- Jellyfin.Networking/Manager/NetworkManager.cs | 19 +++++++++++++++---- Jellyfin.Server/Program.cs | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 9c7d096a7..d2e9dcf9e 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -285,14 +285,25 @@ namespace Jellyfin.Networking.Manager // No bind address and no exclusions, so listen on all interfaces. Collection result = new Collection(); - if (IsIP4Enabled) + if (IsIP6Enabled && IsIP4Enabled) + { + // Kestrel source code shows it uses Sockets.DualMode - so this also covers IPAddress.Any + result.AddItem(IPAddress.IPv6Any); + } + else if (IsIP4Enabled) { result.AddItem(IPAddress.Any); } - - if (IsIP6Enabled) + else if (IsIP6Enabled) { - result.AddItem(IPAddress.IPv6Any); + // Cannot use IPv6Any as Kestrel will bind to IPv4 addresses. + foreach (var iface in _interfaceAddresses) + { + if (iface.AddressFamily == AddressFamily.InterNetworkV6) + { + result.AddItem(iface.Address); + } + } } return result; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index f05cdfe9b..aee53b986 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -280,7 +280,7 @@ namespace Jellyfin.Server bool flagged = false; foreach (IPObject netAdd in addresses) { - _logger.LogInformation("Kestrel listening on {0}", netAdd); + _logger.LogInformation("Kestrel listening on {0}", netAdd.Address == IPAddress.IPv6Any ? "All Addresses" : netAdd); options.Listen(netAdd.Address, appHost.HttpPort); if (appHost.ListenWithHttps) { From b5e3c02865b4fc4953c317f647b7319db34922c8 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Mon, 1 Mar 2021 19:37:46 -0500 Subject: [PATCH 028/402] Move IHasPermissions.cs to correct namespace --- Jellyfin.Data/Interfaces/IHasPermissions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Data/Interfaces/IHasPermissions.cs b/Jellyfin.Data/Interfaces/IHasPermissions.cs index 3be72259a..85ee12ad7 100644 --- a/Jellyfin.Data/Interfaces/IHasPermissions.cs +++ b/Jellyfin.Data/Interfaces/IHasPermissions.cs @@ -2,7 +2,7 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; -namespace Jellyfin.Data +namespace Jellyfin.Data.Interfaces { /// /// An abstraction representing an entity that has permissions. From c275c5c1ea12a73c90b0f3599916eb48e21e130c Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 2 Mar 2021 08:57:27 +0000 Subject: [PATCH 029/402] Update Jellyfin.Server/Program.cs Co-authored-by: Claus Vium --- Jellyfin.Server/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index aee53b986..d2ab11be4 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -280,7 +280,7 @@ namespace Jellyfin.Server bool flagged = false; foreach (IPObject netAdd in addresses) { - _logger.LogInformation("Kestrel listening on {0}", netAdd.Address == IPAddress.IPv6Any ? "All Addresses" : netAdd); + _logger.LogInformation("Kestrel listening on {Address}", netAdd.Address == IPAddress.IPv6Any ? "All Addresses" : netAdd); options.Listen(netAdd.Address, appHost.HttpPort); if (appHost.ListenWithHttps) { From 8f99bdd07ce037b633993f06f5a68dcb293d6828 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 2 Mar 2021 21:17:25 +0100 Subject: [PATCH 030/402] Fix TMDb search name containing year (#5349) --- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 16 ++++++---------- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 14 +++++++++----- .../Plugins/Tmdb/TmdbClientManager.cs | 5 +++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index 93998a110..b455e5634 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -111,10 +111,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var item = new Episode { - Name = info.Name, IndexNumber = info.IndexNumber, ParentIndexNumber = info.ParentIndexNumber, - IndexNumberEnd = info.IndexNumberEnd + IndexNumberEnd = info.IndexNumberEnd, + Name = episodeResult.Name, + PremiereDate = episodeResult.AirDate, + ProductionYear = episodeResult.AirDate?.Year, + Overview = episodeResult.Overview, + CommunityRating = Convert.ToSingle(episodeResult.VoteAverage) }; if (!string.IsNullOrEmpty(episodeResult.ExternalIds?.TvdbId)) @@ -122,14 +126,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV item.SetProviderId(MetadataProvider.Tvdb, episodeResult.ExternalIds.TvdbId); } - item.PremiereDate = episodeResult.AirDate; - item.ProductionYear = episodeResult.AirDate?.Year; - - item.Name = episodeResult.Name; - item.Overview = episodeResult.Overview; - - item.CommunityRating = Convert.ToSingle(episodeResult.VoteAverage); - if (episodeResult.Videos?.Results != null) { foreach (var video in episodeResult.Videos.Results) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 942c85b90..0238d0574 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -22,15 +23,17 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public class TmdbSeriesProvider : IRemoteMetadataProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; + private readonly ILibraryManager _libraryManager; private readonly TmdbClientManager _tmdbClientManager; public TmdbSeriesProvider( + ILibraryManager libraryManager, IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { + _libraryManager = libraryManager; _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; - Current = this; } public string Name => TmdbUtils.ProviderName; @@ -38,8 +41,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // After TheTVDB public int Order => 1; - internal static TmdbSeriesProvider Current { get; private set; } - public async Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); @@ -104,7 +105,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } } - var tvSearchResults = await _tmdbClientManager.SearchSeriesAsync(searchInfo.Name, searchInfo.MetadataLanguage, cancellationToken) + var tvSearchResults = await _tmdbClientManager.SearchSeriesAsync(searchInfo.Name, searchInfo.MetadataLanguage, cancellationToken: cancellationToken) .ConfigureAwait(false); var remoteResults = new RemoteSearchResult[tvSearchResults.Count]; @@ -203,7 +204,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (string.IsNullOrEmpty(tmdbId)) { result.QueriedById = false; - var searchResults = await _tmdbClientManager.SearchSeriesAsync(info.Name, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + // ParseName is required here. + // Caller provides the filename with extension stripped and NOT the parsed filename + var parsedName = _libraryManager.ParseName(info.Name); + var searchResults = await _tmdbClientManager.SearchSeriesAsync(parsedName.Name, info.MetadataLanguage, info.Year ?? 0, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index 2dc5cd55d..bf0f027fc 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -278,9 +278,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// /// The name of the tv show. /// The tv show's language. + /// The year the tv show first aired. /// The cancellation token. /// The TMDb tv show information. - public async Task> SearchSeriesAsync(string name, string language, CancellationToken cancellationToken) + public async Task> SearchSeriesAsync(string name, string language, int year = 0, CancellationToken cancellationToken = default) { var key = $"searchseries-{name}-{language}"; if (_memoryCache.TryGetValue(key, out SearchContainer series)) @@ -291,7 +292,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); var searchResults = await _tmDbClient - .SearchTvShowAsync(name, TmdbUtils.NormalizeLanguage(language), cancellationToken: cancellationToken) + .SearchTvShowAsync(name, TmdbUtils.NormalizeLanguage(language), firstAirDateYear: year, cancellationToken: cancellationToken) .ConfigureAwait(false); if (searchResults.Results.Count > 0) From bfe84affb32f798b6f2b2a6a4306065fc8800b1a Mon Sep 17 00:00:00 2001 From: Nathan Mascitelli Date: Tue, 2 Mar 2021 22:20:41 -0500 Subject: [PATCH 031/402] Update README to include ffmpeg --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 29f992349..5af312ad7 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,8 @@ Before the project can be built, you must first install the [.NET 5.0 SDK](https Instructions to run this project from the command line are included here, but you will also need to install an IDE if you want to debug the server while it is running. Any IDE that supports .NET Core development will work, but two options are recent versions of [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2017) and [Visual Studio Code](https://code.visualstudio.com/Download). +[ffmpeg](https://ffmpeg.org/download.html) will also need to be installed. + ### Cloning the Repository After dependencies are installed you will need to clone a local copy of this repository. If you just want to run the server from source you can clone this repository directly, but if you are intending to contribute code changes to the project, you should [set up your own fork](https://jellyfin.org/docs/general/contributing/development.html#set-up-your-copy-of-the-repo) of the repository. The following example shows how you can clone the repository directly over HTTPS. From 664c5da31728e65d0e53ada7c06c918059f73615 Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 3 Mar 2021 09:09:57 +0100 Subject: [PATCH 032/402] return false when providerid is null or empty --- MediaBrowser.Model/Entities/ProviderIdsExtensions.cs | 8 +++++--- MediaBrowser.Providers/Manager/ProviderManager.cs | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index bde5a1da1..571bc7006 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -50,13 +50,15 @@ namespace MediaBrowser.Model.Entities throw new ArgumentNullException(nameof(instance)); } - if (instance.ProviderIds == null) + var foundProviderId = instance.ProviderIds.TryGetValue(name, out id); + // This occurs when searching with Identify (and possibly in other places) + if (string.IsNullOrEmpty(id)) { id = null; - return false; + foundProviderId = false; } - return instance.ProviderIds.TryGetValue(name, out id); + return foundProviderId; } /// diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 913f14d9b..bc16a8abb 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -869,14 +869,14 @@ namespace MediaBrowser.Providers.Manager } } } - catch (Exception) +#pragma warning disable CA1031 // do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // do not catch general exception types { - // Logged at lower levels + _logger.LogError(ex, "Provider {ProviderName} failed to retrieve search results", provider.Name); } } - // _logger.LogDebug("Returning search results {0}", _json.SerializeToString(resultList)); - return resultList; } From a49f5d2a441d53076c0eae27fc115a97691f4856 Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 3 Mar 2021 09:37:21 +0100 Subject: [PATCH 033/402] revert removal of null check --- MediaBrowser.Model/Entities/ProviderIdsExtensions.cs | 6 ++++++ .../Entities/ProviderIdsExtensionsTests.cs | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 571bc7006..3086fcefd 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -50,6 +50,12 @@ namespace MediaBrowser.Model.Entities throw new ArgumentNullException(nameof(instance)); } + if (instance.ProviderIds == null) + { + id = null; + return false; + } + var foundProviderId = instance.ProviderIds.TryGetValue(name, out id); // This occurs when searching with Identify (and possibly in other places) if (string.IsNullOrEmpty(id)) diff --git a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs index 2b2414ef1..cf9fb15d7 100644 --- a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs @@ -18,7 +18,7 @@ namespace Jellyfin.Model.Tests.Entities [Fact] public void HasProviderId_NullProvider_False() { - var nullProvider = new ProviderIdsExtensionsTestsObject() + var nullProvider = new ProviderIdsExtensionsTestsObject { ProviderIds = null! }; @@ -68,7 +68,7 @@ namespace Jellyfin.Model.Tests.Entities [Fact] public void GetProviderId_NullProvider_Null() { - var nullProvider = new ProviderIdsExtensionsTestsObject() + var nullProvider = new ProviderIdsExtensionsTestsObject { ProviderIds = null! }; @@ -85,7 +85,7 @@ namespace Jellyfin.Model.Tests.Entities [Fact] public void TryGetProviderId_NullProvider_False() { - var nullProvider = new ProviderIdsExtensionsTestsObject() + var nullProvider = new ProviderIdsExtensionsTestsObject { ProviderIds = null! }; @@ -146,7 +146,7 @@ namespace Jellyfin.Model.Tests.Entities [Fact] public void SetProviderId_NullProvider_Success() { - var nullProvider = new ProviderIdsExtensionsTestsObject() + var nullProvider = new ProviderIdsExtensionsTestsObject { ProviderIds = null! }; @@ -158,7 +158,7 @@ namespace Jellyfin.Model.Tests.Entities [Fact] public void SetProviderId_NullProviderAndEmptyName_Success() { - var nullProvider = new ProviderIdsExtensionsTestsObject() + var nullProvider = new ProviderIdsExtensionsTestsObject { ProviderIds = null! }; From 8b72b902f53b32c0c0d69eeda3bd32f992ac53ee Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 3 Mar 2021 12:28:40 +0100 Subject: [PATCH 034/402] fix HasProviderId and add tests --- .../Entities/ProviderIdsExtensions.cs | 2 +- .../Entities/ProviderIdsExtensionsTests.cs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 3086fcefd..09d14dc6a 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Entities throw new ArgumentNullException(nameof(instance)); } - return instance.ProviderIds?.ContainsKey(name) ?? false; + return instance.TryGetProviderId(name, out _); } /// diff --git a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs index cf9fb15d7..a1ace8476 100644 --- a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs @@ -47,6 +47,15 @@ namespace Jellyfin.Model.Tests.Entities Assert.True(provider.HasProviderId(MetadataProvider.Imdb)); } + [Fact] + public void HasProviderId_FoundNameEmptyValue_False() + { + var provider = new ProviderIdsExtensionsTestsObject(); + provider.ProviderIds[MetadataProvider.Imdb.ToString()] = string.Empty; + + Assert.False(provider.HasProviderId(MetadataProvider.Imdb)); + } + [Fact] public void GetProviderId_NullInstance_ThrowsArgumentNullException() { @@ -112,6 +121,16 @@ namespace Jellyfin.Model.Tests.Entities Assert.Equal(ExampleImdbId, id); } + [Fact] + public void TryGetProviderId_FoundNameEmptyValue_False() + { + var provider = new ProviderIdsExtensionsTestsObject(); + provider.ProviderIds[MetadataProvider.Imdb.ToString()] = string.Empty; + + Assert.False(provider.TryGetProviderId(MetadataProvider.Imdb, out var id)); + Assert.Null(id); + } + [Fact] public void SetProviderId_NullInstance_ThrowsArgumentNullException() { From 610a30d791ab11b3d815cbf7e361198f30fdafd5 Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 3 Mar 2021 15:13:43 +0100 Subject: [PATCH 035/402] Do nothing in timer callback when device locator is disposed --- RSSDP/SsdpDeviceLocator.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index bfad6de97..0cdc5ce3d 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -97,6 +97,11 @@ namespace Rssdp.Infrastructure private async void OnBroadcastTimerCallback(object state) { + if (IsDisposed) + { + return; + } + StartListeningForNotifications(); RemoveExpiredDevicesFromCache(); @@ -180,8 +185,6 @@ namespace Rssdp.Infrastructure /// Throw if the ty is true. public void StartListeningForNotifications() { - ThrowIfDisposed(); - _CommunicationsServer.RequestReceived -= CommsServer_RequestReceived; _CommunicationsServer.RequestReceived += CommsServer_RequestReceived; _CommunicationsServer.BeginListeningForBroadcasts(); @@ -353,7 +356,7 @@ namespace Rssdp.Infrastructure { return; } - + var location = GetFirstHeaderUriValue("Location", message); if (location != null) { @@ -515,11 +518,6 @@ namespace Rssdp.Infrastructure private void RemoveExpiredDevicesFromCache() { - if (this.IsDisposed) - { - return; - } - DiscoveredSsdpDevice[] expiredDevices = null; lock (_Devices) { From d819a1d92827f6a8c8b3b5289a4ca306a9f098ed Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 3 Mar 2021 14:41:18 +0000 Subject: [PATCH 036/402] Remove Content-Length header from DLNA HEAD request (#5335) --- Emby.Dlna/PlayTo/PlayToController.cs | 2 +- Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index e4923b9eb..5abc1bc13 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -132,7 +132,7 @@ namespace Emby.Dlna.PlayTo private async void OnDeviceMediaChanged(object sender, MediaChangedEventArgs e) { - if (_disposed) + if (_disposed || string.IsNullOrEmpty(e.OldMediaInfo.Url)) { return; } diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index 89d36ab09..f828b1d9d 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -107,7 +107,8 @@ namespace Jellyfin.Api.Helpers // Headers only if (isHeadRequest) { - return new FileContentResult(Array.Empty(), contentType); + httpContext.Response.Headers[HeaderNames.ContentType] = contentType; + return new OkResult(); } var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath); From 631c0a35f61e9c9ce3e6decfe369faf2c73f3f62 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 3 Mar 2021 17:11:19 -0700 Subject: [PATCH 037/402] Always use case insensitive json parsing for api --- Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 77f6695bb..1828f1a7e 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -232,7 +232,6 @@ namespace Jellyfin.Server.Extensions options.JsonSerializerOptions.WriteIndented = jsonOptions.WriteIndented; options.JsonSerializerOptions.DefaultIgnoreCondition = jsonOptions.DefaultIgnoreCondition; options.JsonSerializerOptions.NumberHandling = jsonOptions.NumberHandling; - options.JsonSerializerOptions.PropertyNameCaseInsensitive = jsonOptions.PropertyNameCaseInsensitive; options.JsonSerializerOptions.Converters.Clear(); foreach (var converter in jsonOptions.Converters) From 858c91ab485f79648aa625e89378c6de9e19df4b Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 4 Mar 2021 18:25:52 -0700 Subject: [PATCH 038/402] Specify defaults or set query parameter to nullable --- Jellyfin.Api/Controllers/AudioController.cs | 8 ++-- .../Controllers/DynamicHlsController.cs | 48 +++++++++---------- .../Controllers/HlsSegmentController.cs | 4 +- Jellyfin.Api/Controllers/ImageController.cs | 16 +++---- .../Controllers/ItemUpdateController.cs | 2 +- Jellyfin.Api/Controllers/LibraryController.cs | 2 +- .../Controllers/PlaystateController.cs | 20 ++++---- .../Controllers/UniversalAudioController.cs | 2 +- .../Controllers/VideoHlsController.cs | 8 ++-- Jellyfin.Api/Controllers/VideosController.cs | 12 ++--- 10 files changed, 62 insertions(+), 60 deletions(-) diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 616fe5b91..8b1813b20 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -122,7 +122,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -174,7 +174,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -287,7 +287,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -339,7 +339,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index e375645cf..f6c23c5aa 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -203,7 +203,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -218,7 +218,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, [FromQuery] bool enableAdaptiveBitrateStreaming = true) { @@ -255,7 +255,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -270,7 +270,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming }; @@ -370,7 +370,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -385,7 +385,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, [FromQuery] bool enableAdaptiveBitrateStreaming = true) { @@ -422,7 +422,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -437,7 +437,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming }; @@ -533,7 +533,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -548,7 +548,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions) { var cancellationTokenSource = new CancellationTokenSource(); @@ -585,7 +585,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -600,7 +600,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions }; @@ -698,7 +698,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -713,7 +713,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions) { var cancellationTokenSource = new CancellationTokenSource(); @@ -750,7 +750,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -765,7 +765,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions }; @@ -868,7 +868,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -883,7 +883,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions) { var streamingRequest = new VideoRequestDto @@ -920,7 +920,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -935,7 +935,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions }; @@ -1040,7 +1040,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -1055,7 +1055,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions) { var streamingRequest = new StreamingRequestDto @@ -1092,7 +1092,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -1107,7 +1107,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions }; diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 25abe73ed..d0ed45acb 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -96,7 +96,9 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Videos/ActiveEncodings")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult StopEncodingProcess([FromQuery] string deviceId, [FromQuery] string playSessionId) + public ActionResult StopEncodingProcess( + [FromQuery, Required] string deviceId, + [FromQuery, Required] string playSessionId) { _transcodingJobHelper.KillTranscodingJobs(deviceId, playSessionId, path => true); return NoContent(); diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index a50d6e46b..cfc038f23 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -392,7 +392,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid itemId, [FromRoute, Required] ImageType imageType, [FromRoute, Required] int imageIndex, - [FromQuery] int newIndex) + [FromQuery, Required] int newIndex) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -741,7 +741,7 @@ namespace Jellyfin.Api.Controllers public async Task GetArtistImage( [FromRoute, Required] string name, [FromRoute, Required] ImageType imageType, - [FromQuery] string tag, + [FromQuery] string? tag, [FromQuery] ImageFormat? format, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, @@ -820,7 +820,7 @@ namespace Jellyfin.Api.Controllers public async Task GetGenreImage( [FromRoute, Required] string name, [FromRoute, Required] ImageType imageType, - [FromQuery] string tag, + [FromQuery] string? tag, [FromQuery] ImageFormat? format, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, @@ -900,7 +900,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string name, [FromRoute, Required] ImageType imageType, [FromRoute, Required] int imageIndex, - [FromQuery] string tag, + [FromQuery] string? tag, [FromQuery] ImageFormat? format, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, @@ -978,7 +978,7 @@ namespace Jellyfin.Api.Controllers public async Task GetMusicGenreImage( [FromRoute, Required] string name, [FromRoute, Required] ImageType imageType, - [FromQuery] string tag, + [FromQuery] string? tag, [FromQuery] ImageFormat? format, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, @@ -1058,7 +1058,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string name, [FromRoute, Required] ImageType imageType, [FromRoute, Required] int imageIndex, - [FromQuery] string tag, + [FromQuery] string? tag, [FromQuery] ImageFormat? format, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, @@ -1136,7 +1136,7 @@ namespace Jellyfin.Api.Controllers public async Task GetPersonImage( [FromRoute, Required] string name, [FromRoute, Required] ImageType imageType, - [FromQuery] string tag, + [FromQuery] string? tag, [FromQuery] ImageFormat? format, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, @@ -1216,7 +1216,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string name, [FromRoute, Required] ImageType imageType, [FromRoute, Required] int imageIndex, - [FromQuery] string tag, + [FromQuery] string? tag, [FromQuery] ImageFormat? format, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 9e1a39853..a9f4a5a58 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -195,7 +195,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/ContentType")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItemContentType([FromRoute, Required] Guid itemId, [FromQuery] string contentType) + public ActionResult UpdateItemContentType([FromRoute, Required] Guid itemId, [FromQuery] string? contentType) { var item = _libraryManager.GetItemById(itemId); if (item == null) diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index db4aa9668..3443ebd72 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -777,7 +777,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetLibraryOptionsInfo( [FromQuery] string? libraryContentType, - [FromQuery] bool isNewLibrary) + [FromQuery] bool isNewLibrary = false) { var result = new LibraryOptionsResultDto(); diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index ec7b84ff6..f256c8c25 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -152,7 +152,7 @@ namespace Jellyfin.Api.Controllers /// A . [HttpPost("Sessions/Playing/Ping")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult PingPlaybackSession([FromQuery] string playSessionId) + public ActionResult PingPlaybackSession([FromQuery, Required] string playSessionId) { _transcodingJobHelper.PingTranscodingJob(playSessionId, null); return NoContent(); @@ -202,9 +202,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? mediaSourceId, [FromQuery] int? audioStreamIndex, [FromQuery] int? subtitleStreamIndex, - [FromQuery] PlayMethod playMethod, + [FromQuery] PlayMethod? playMethod, [FromQuery] string? liveStreamId, - [FromQuery] string playSessionId, + [FromQuery] string? playSessionId, [FromQuery] bool canSeek = false) { var playbackStartInfo = new PlaybackStartInfo @@ -214,7 +214,7 @@ namespace Jellyfin.Api.Controllers MediaSourceId = mediaSourceId, AudioStreamIndex = audioStreamIndex, SubtitleStreamIndex = subtitleStreamIndex, - PlayMethod = playMethod, + PlayMethod = playMethod ?? PlayMethod.Transcode, PlaySessionId = playSessionId, LiveStreamId = liveStreamId }; @@ -254,10 +254,10 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? audioStreamIndex, [FromQuery] int? subtitleStreamIndex, [FromQuery] int? volumeLevel, - [FromQuery] PlayMethod playMethod, + [FromQuery] PlayMethod? playMethod, [FromQuery] string? liveStreamId, - [FromQuery] string playSessionId, - [FromQuery] RepeatMode repeatMode, + [FromQuery] string? playSessionId, + [FromQuery] RepeatMode? repeatMode, [FromQuery] bool isPaused = false, [FromQuery] bool isMuted = false) { @@ -271,10 +271,10 @@ namespace Jellyfin.Api.Controllers AudioStreamIndex = audioStreamIndex, SubtitleStreamIndex = subtitleStreamIndex, VolumeLevel = volumeLevel, - PlayMethod = playMethod, + PlayMethod = playMethod ?? PlayMethod.Transcode, PlaySessionId = playSessionId, LiveStreamId = liveStreamId, - RepeatMode = repeatMode + RepeatMode = repeatMode ?? RepeatMode.RepeatNone }; playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); @@ -352,7 +352,7 @@ namespace Jellyfin.Api.Controllers return _userDataRepository.GetUserDataDto(item, user); } - private PlayMethod ValidatePlayMethod(PlayMethod method, string playSessionId) + private PlayMethod ValidatePlayMethod(PlayMethod method, string? playSessionId) { if (method == PlayMethod.Transcode) { diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 5aa033ccf..0c2e6f19f 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -112,7 +112,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? maxAudioSampleRate, [FromQuery] int? maxAudioBitDepth, [FromQuery] bool? enableRemoteMedia, - [FromQuery] bool breakOnNonKeyFrames, + [FromQuery] bool breakOnNonKeyFrames = false, [FromQuery] bool enableRedirection = true) { var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index ba51aa43e..620eef568 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -198,7 +198,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -213,7 +213,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, @@ -253,7 +253,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -268,7 +268,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, MaxHeight = maxHeight, MaxWidth = maxWidth, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 44dc63952..8c7aa7325 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -364,7 +364,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -379,7 +379,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions) { var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head; @@ -418,7 +418,7 @@ namespace Jellyfin.Api.Controllers Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? true, @@ -433,7 +433,7 @@ namespace Jellyfin.Api.Controllers TranscodeReasons = transcodeReasons, AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, - Context = context, + Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions }; @@ -620,7 +620,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? height, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, - [FromQuery] SubtitleDeliveryMethod subtitleMethod, + [FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] int? maxRefFrames, [FromQuery] int? maxVideoBitDepth, [FromQuery] bool? requireAvc, @@ -635,7 +635,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, - [FromQuery] EncodingContext context, + [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions) { return GetVideoStream( From ba366118f9c9853153a90b7b5b22f0acd40867ed Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 5 Mar 2021 08:18:04 +0100 Subject: [PATCH 039/402] Do not use language or imagelanguages when searching for images TMDb API returns all images if languages are excluded, which is needed for the All Languages toggle in Identify. --- MediaBrowser.Providers/Manager/ProviderManager.cs | 1 + .../Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 3 ++- .../Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs | 3 ++- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 3 ++- .../Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs | 3 ++- .../Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs | 3 ++- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index bc16a8abb..d581dd434 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -242,6 +242,7 @@ namespace MediaBrowser.Providers.Manager languages.Add(preferredLanguage); } + // TODO include [query.IncludeAllLanguages] as an argument to the providers var tasks = providers.Select(i => GetImages(item, i, languages, cancellationToken, query.ImageType)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index df1e12240..5ad61c567 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -58,7 +58,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets var language = item.GetPreferredMetadataLanguage(); - var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken).ConfigureAwait(false); + // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here + var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, null, null, cancellationToken).ConfigureAwait(false); if (collection?.Images == null) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs index dac9e961c..f34d689c1 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs @@ -73,8 +73,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies return Enumerable.Empty(); } + // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var movie = await _tmdbClientManager - .GetMovieAsync(movieTmdbId, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken) + .GetMovieAsync(movieTmdbId, null, null, cancellationToken) .ConfigureAwait(false); if (movie?.Images == null) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 3b7a0b254..d92336624 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -63,8 +63,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var language = item.GetPreferredMetadataLanguage(); + // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var episodeResult = await _tmdbClientManager - .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken) + .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, null, null, cancellationToken) .ConfigureAwait(false); var stills = episodeResult?.Images?.Stills; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index f4ed480ae..0d23c7872 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -52,8 +52,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var language = item.GetPreferredMetadataLanguage(); + // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var seasonResult = await _tmdbClientManager - .GetSeasonAsync(seriesTmdbId, season.IndexNumber.Value, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken) + .GetSeasonAsync(seriesTmdbId, season.IndexNumber.Value, null, null, cancellationToken) .ConfigureAwait(false); var posters = seasonResult?.Images?.Posters; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index d0c6b8b88..a96fc8ed6 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -59,8 +59,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var language = item.GetPreferredMetadataLanguage(); + // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var series = await _tmdbClientManager - .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken) + .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), null, null, cancellationToken) .ConfigureAwait(false); if (series?.Images == null) From ed7154db68f089cb8366aedaaeb93f1d5405251a Mon Sep 17 00:00:00 2001 From: Nichgon Date: Fri, 5 Mar 2021 07:42:03 +0000 Subject: [PATCH 040/402] Translated using Weblate (Thai) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/th/ --- Emby.Server.Implementations/Localization/Core/th.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json index 71dd2c7a3..5bf58baf8 100644 --- a/Emby.Server.Implementations/Localization/Core/th.json +++ b/Emby.Server.Implementations/Localization/Core/th.json @@ -50,7 +50,7 @@ "HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ", "HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ", "HeaderContinueWatching": "ดูต่อ", - "HeaderAlbumArtists": "อัลบั้มศิลปิน", + "HeaderAlbumArtists": "ศิลปินอัลบั้ม", "Genres": "ประเภท", "Folders": "โฟลเดอร์", "Favorites": "รายการโปรด", @@ -112,5 +112,6 @@ "System": "ระบบ", "Sync": "ซิงค์", "SubtitleDownloadFailureFromForItem": "ไม่สามารถดาวน์โหลดคำบรรยายจาก {0} สำหรับ {1} ได้", - "StartupEmbyServerIsLoading": "กำลังโหลดเซิร์ฟเวอร์ Jellyfin โปรดลองอีกครั้งในอีกสักครู่" + "StartupEmbyServerIsLoading": "กำลังโหลดเซิร์ฟเวอร์ Jellyfin โปรดลองอีกครั้งในอีกสักครู่", + "Default": "ค่าเริ่มต้น" } From a6d0db5d0409f72e2abb02291268b6971eaf1fcf Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 5 Mar 2021 11:15:14 +0100 Subject: [PATCH 041/402] 100% branch coverage for DashboardController --- Emby.Server.Implementations/Plugins/PluginManager.cs | 4 ++-- Jellyfin.Api/Controllers/DashboardController.cs | 4 ++-- MediaBrowser.Common/Plugins/IPluginManager.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 7bc9f0a7e..c579fc8cb 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.Plugins private readonly ILogger _logger; private readonly IApplicationHost _appHost; private readonly ServerConfiguration _config; - private readonly IList _plugins; + private readonly List _plugins; private readonly Version _minimumVersion; private IHttpClientFactory? _httpClientFactory; @@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.Plugins /// /// Gets the Plugins. /// - public IList Plugins => _plugins; + public IReadOnlyList Plugins => _plugins; /// /// Returns all the assemblies. diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs index a2c2ecd66..445733c24 100644 --- a/Jellyfin.Api/Controllers/DashboardController.cs +++ b/Jellyfin.Api/Controllers/DashboardController.cs @@ -95,9 +95,9 @@ namespace Jellyfin.Api.Controllers return GetPluginPages(plugin).Select(i => new ConfigurationPageInfo(plugin.Instance, i.Item1)); } - private IEnumerable> GetPluginPages(LocalPlugin? plugin) + private IEnumerable> GetPluginPages(LocalPlugin plugin) { - if (plugin?.Instance is not IHasWebPages hasWebPages) + if (plugin.Instance is not IHasWebPages hasWebPages) { return Enumerable.Empty>(); } diff --git a/MediaBrowser.Common/Plugins/IPluginManager.cs b/MediaBrowser.Common/Plugins/IPluginManager.cs index fc2fcb517..f9a8fb6f7 100644 --- a/MediaBrowser.Common/Plugins/IPluginManager.cs +++ b/MediaBrowser.Common/Plugins/IPluginManager.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Common.Plugins /// /// Gets the Plugins. /// - IList Plugins { get; } + IReadOnlyList Plugins { get; } /// /// Creates the plugins. From 37e374d33d73403470d07d814b5ee1367ca12e85 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 5 Mar 2021 14:09:23 +0100 Subject: [PATCH 042/402] make sure network path substitution matches correctly --- .../Library/LibraryManager.cs | 64 ++++----------- .../Library/PathExtensions.cs | 80 +++++++++++++++++++ .../Library/PathExtensionsTests.cs | 23 ++++++ 3 files changed, 117 insertions(+), 50 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index d9ffe64b3..973b2df8a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2788,10 +2788,9 @@ namespace Emby.Server.Implementations.Library continue; } - var substitutionResult = SubstitutePathInternal(path, pathInfo.Path, pathInfo.NetworkPath); - if (substitutionResult.Item2) + if (path.TryReplaceSubPath(pathInfo.Path, pathInfo.NetworkPath, out var newPath)) { - return substitutionResult.Item1; + return newPath; } } } @@ -2802,22 +2801,22 @@ namespace Emby.Server.Implementations.Library if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath)) { - var metadataSubstitutionResult = SubstitutePathInternal(path, metadataPath, metadataNetworkPath); - if (metadataSubstitutionResult.Item2) + if (path.TryReplaceSubPath(metadataPath, metadataNetworkPath, out var newPath)) { - return metadataSubstitutionResult.Item1; + return newPath; } } foreach (var map in _configurationManager.Configuration.PathSubstitutions) { - if (!string.IsNullOrWhiteSpace(map.From)) + if (string.IsNullOrWhiteSpace(map.From)) { - var substitutionResult = SubstitutePathInternal(path, map.From, map.To); - if (substitutionResult.Item2) - { - return substitutionResult.Item1; - } + continue; + } + + if (path.TryReplaceSubPath(map.From, map.To, out var newPath)) + { + return newPath; } } @@ -2826,47 +2825,12 @@ namespace Emby.Server.Implementations.Library public string SubstitutePath(string path, string from, string to) { - return SubstitutePathInternal(path, from, to).Item1; - } - - private Tuple SubstitutePathInternal(string path, string from, string to) - { - if (string.IsNullOrWhiteSpace(path)) + if (path.TryReplaceSubPath(from, to, out var newPath)) { - throw new ArgumentNullException(nameof(path)); + return newPath; } - if (string.IsNullOrWhiteSpace(from)) - { - throw new ArgumentNullException(nameof(from)); - } - - if (string.IsNullOrWhiteSpace(to)) - { - throw new ArgumentNullException(nameof(to)); - } - - from = from.Trim(); - to = to.Trim(); - - var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase); - var changed = false; - - if (!string.Equals(newPath, path, StringComparison.Ordinal)) - { - if (to.IndexOf('/', StringComparison.Ordinal) != -1) - { - newPath = newPath.Replace('\\', '/'); - } - else - { - newPath = newPath.Replace('/', '\\'); - } - - changed = true; - } - - return new Tuple(newPath, changed); + return path; } private void SetExtraTypeFromFilename(Video item) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 06ff3e611..1fc5526ae 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -1,6 +1,7 @@ #nullable enable using System; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace Emby.Server.Implementations.Library @@ -47,5 +48,84 @@ namespace Emby.Server.Implementations.Library return null; } + + /// + /// Replaces a sub path with another sub path and normalizes the final path. + /// + /// The original path. + /// The original sub path. + /// The new sub path. + /// The result of the sub path replacement + /// The path after replacing the sub path. + /// , or is empty. + public static bool TryReplaceSubPath(this string path, string subPath, string newSubPath, [NotNullWhen(true)] out string? newPath) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException(nameof(path)); + } + + if (string.IsNullOrWhiteSpace(subPath)) + { + throw new ArgumentNullException(nameof(subPath)); + } + + if (string.IsNullOrWhiteSpace(newSubPath)) + { + throw new ArgumentNullException(nameof(newSubPath)); + } + + char oldDirectorySeparatorChar; + char newDirectorySeparatorChar; + // True normalization is still not possible https://github.com/dotnet/runtime/issues/2162 + // The reasoning behind this is that a forward slash likely means it's a Linux path and + // so the whole path should be normalized to use / and vice versa for Windows (although Windows doesn't care much). + if (newSubPath.Contains('/', StringComparison.Ordinal)) + { + oldDirectorySeparatorChar = '\\'; + newDirectorySeparatorChar = '/'; + } + else + { + oldDirectorySeparatorChar = '/'; + newDirectorySeparatorChar = '\\'; + } + + if (path.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal)) + { + path = path.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); + } + + if (subPath.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal)) + { + subPath = subPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); + } + + // We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results + // when the sub path matches a similar but in-complete subpath + if (!subPath.EndsWith(newDirectorySeparatorChar)) + { + subPath += newDirectorySeparatorChar; + } + + if (newSubPath.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal)) + { + newSubPath = newSubPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); + } + + if (!newSubPath.EndsWith(newDirectorySeparatorChar)) + { + newSubPath += newDirectorySeparatorChar; + } + + if (!path.Contains(subPath, StringComparison.OrdinalIgnoreCase)) + { + newPath = null; + return false; + } + + newPath = path.Replace(subPath, newSubPath, StringComparison.OrdinalIgnoreCase); + return true; + } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index 6d768af89..a96a05377 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -24,5 +24,28 @@ namespace Jellyfin.Server.Implementations.Tests.Library { Assert.Throws(() => PathExtensions.GetAttributeValue(input, attribute)); } + + [Theory] + [InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff", "/home/jeff", true, "/home/jeff/myfile.mkv")] + [InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff/", "/home/jeff", true, "/home/jeff/myfile.mkv")] + [InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/not jeff's band", "/home/not jeff", false, null)] + [InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/jeff's band", "/home/not jeff", true, "/home/not jeff/consistently inconsistent.mp3")] + [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff", true, "/home/jeff/myfile.mkv")] + public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, bool succeeded, string? expectedResult) + { + var status = PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result); + Assert.Equal(succeeded, status); + Assert.Equal(expectedResult, result); + } + + [Theory] + [InlineData("", "", "")] + [InlineData("/my/path", "", "")] + [InlineData("", "/another/path", "")] + [InlineData("", "", "/new/subpath")] + public void TryReplaceSubPath_EmptyString_ThrowsArgumentNullException(string path, string subPath, string newSubPath) + { + Assert.Throws(() => PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out _)); + } } } From fd0b3ca5ef82164f5551369b789b9677f30af172 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 5 Mar 2021 07:48:45 -0700 Subject: [PATCH 043/402] Add JsonVersionConverter and tests --- .../Json/Converters/JsonVersionConverter.cs | 23 ++++++++++++ MediaBrowser.Common/Json/JsonDefaults.cs | 1 + .../MediaBrowser.Common.csproj | 2 +- .../Json/JsonVersionConverterTests.cs | 36 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs create mode 100644 tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs new file mode 100644 index 000000000..b8aecf8b8 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Converts a Version object or value to/from JSON. + /// + /// + /// Required to send as a string instead of an object. + /// + public class JsonVersionConverter : JsonConverter + { + /// + public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => new Version(reader.GetString()); + + /// + public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index a3999e7e2..2ef24a884 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -35,6 +35,7 @@ namespace MediaBrowser.Common.Json { new JsonGuidConverter(), new JsonNullableGuidConverter(), + new JsonVersionConverter(), new JsonStringEnumConverter(), new JsonNullableStructConverterFactory(), new JsonBoolNumberConverter(), diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index e469436a9..8bb30c565 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -14,7 +14,7 @@ - + diff --git a/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs new file mode 100644 index 000000000..83dfb0a4f --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using MediaBrowser.Common.Json.Converters; +using Xunit; + +namespace Jellyfin.Common.Tests.Json +{ + public class JsonVersionConverterTests + { + private readonly JsonSerializerOptions _options; + + public JsonVersionConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new JsonVersionConverter()); + } + + [Fact] + public void Deserialize_Version_Success() + { + var input = "\"1.025.222\""; + var output = new Version(1, 25, 222); + var deserializedInput = JsonSerializer.Deserialize(input, _options); + Assert.Equal(output, deserializedInput); + } + + [Fact] + public void Serialize_Version_Success() + { + var input = new Version(1, 09, 59); + var output = "\"1.9.59\""; + var serializedInput = JsonSerializer.Serialize(input, _options); + Assert.Equal(output, serializedInput); + } + } +} \ No newline at end of file From df1951cfe2baae539758b9592af1a16e51b69897 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 5 Mar 2021 08:30:49 -0700 Subject: [PATCH 044/402] Apply suggestions from code review Co-authored-by: dkanada --- MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs | 2 +- tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs index b8aecf8b8..f69e868cc 100644 --- a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs @@ -20,4 +20,4 @@ namespace MediaBrowser.Common.Json.Converters public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString()); } -} \ No newline at end of file +} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs index 83dfb0a4f..f2cefdbf8 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs @@ -33,4 +33,4 @@ namespace Jellyfin.Common.Tests.Json Assert.Equal(output, serializedInput); } } -} \ No newline at end of file +} From d0a2d00b29d87e7f71e85efd38d2b6c8beafe4ea Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 5 Mar 2021 16:56:21 +0100 Subject: [PATCH 045/402] Fix UpdateMediaPath model binding (#5378) --- .../Controllers/LibraryStructureController.cs | 13 ++++------- .../UpdateMediaPathRequestDto.cs | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs index 328efea26..be9127dd3 100644 --- a/Jellyfin.Api/Controllers/LibraryStructureController.cs +++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs @@ -241,23 +241,20 @@ namespace Jellyfin.Api.Controllers /// /// Updates a media path. /// - /// The name of the library. - /// The path info. + /// The name of the library and path infos. /// A . /// Media path updated. /// The name of the library may not be empty. [HttpPost("Paths/Update")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult UpdateMediaPath( - [FromQuery] string? name, - [FromBody] MediaPathInfo? pathInfo) + public ActionResult UpdateMediaPath([FromBody, Required] UpdateMediaPathRequestDto mediaPathRequestDto) { - if (string.IsNullOrWhiteSpace(name)) + if (string.IsNullOrWhiteSpace(mediaPathRequestDto.Name)) { - throw new ArgumentNullException(nameof(name)); + throw new ArgumentNullException(nameof(mediaPathRequestDto), "Name must not be null or empty"); } - _libraryManager.UpdateMediaPath(name, pathInfo); + _libraryManager.UpdateMediaPath(mediaPathRequestDto.Name, mediaPathRequestDto.PathInfo); return NoContent(); } diff --git a/Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs b/Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs new file mode 100644 index 000000000..fbd4985f9 --- /dev/null +++ b/Jellyfin.Api/Models/LibraryStructureDto/UpdateMediaPathRequestDto.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; +using MediaBrowser.Model.Configuration; + +namespace Jellyfin.Api.Models.LibraryStructureDto +{ + /// + /// Update library options dto. + /// + public class UpdateMediaPathRequestDto + { + /// + /// Gets or sets the library name. + /// + [Required] + public string Name { get; set; } = null!; + + /// + /// Gets or sets library folder path information. + /// + [Required] + public MediaPathInfo PathInfo { get; set; } = null!; + } +} From 59814bd55e8c6c5fbd167e90403b9bd2e390c78c Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 5 Mar 2021 19:57:48 +0100 Subject: [PATCH 046/402] do not pick a linked item as primary when merging versions --- Jellyfin.Api/Controllers/VideosController.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 44dc63952..195a96a56 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -217,9 +217,7 @@ namespace Jellyfin.Api.Controllers return BadRequest("Please supply at least two videos to merge."); } - var videosWithVersions = items.Where(i => i.MediaSourceCount > 1).ToList(); - - var primaryVersion = videosWithVersions.FirstOrDefault(); + var primaryVersion = items.FirstOrDefault(i => i.MediaSourceCount > 1 && string.IsNullOrEmpty(i.PrimaryVersionId)); if (primaryVersion == null) { primaryVersion = items From 6293629d3263722c49c14f7642fb33b2986b0e19 Mon Sep 17 00:00:00 2001 From: Smith00101010 Date: Fri, 5 Mar 2021 22:51:08 +0100 Subject: [PATCH 047/402] Apply suggested formatting changes Co-authored-by: BaronGreenback --- Emby.Server.Implementations/TV/TVSeriesManager.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index d111e1a1c..d3f6fa34d 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -232,7 +232,10 @@ namespace Emby.Server.Implementations.TV IsPlayed = false, IsVirtualItem = false, DtoOptions = dtoOptions - }).Cast().Where(episode => episode.AirsBeforeSeasonNumber != null || episode.AirsAfterSeasonNumber != null).ToList(); + }) + .Cast() + .Where(episode => episode.AirsBeforeSeasonNumber != null || episode.AirsAfterSeasonNumber != null) + .ToList(); if (lastWatchedEpisode != null) { @@ -245,7 +248,8 @@ namespace Emby.Server.Implementations.TV consideredEpisodes.Add(nextEpisode); } - var sortedConsideredEpisodes = _libraryManager.Sort(consideredEpisodes, user, new[] { (ItemSortBy.AiredEpisodeOrder, SortOrder.Ascending) }).Cast(); + var sortedConsideredEpisodes = _libraryManager.Sort(consideredEpisodes, user, new[] { (ItemSortBy.AiredEpisodeOrder, SortOrder.Ascending) }) + .Cast(); if (lastWatchedEpisode != null) { sortedConsideredEpisodes = sortedConsideredEpisodes.SkipWhile(episode => episode.Id != lastWatchedEpisode.Id).Skip(1); From e8413ed8c0cc401447b880dea96e94092bbefe88 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 6 Mar 2021 03:33:52 +0100 Subject: [PATCH 048/402] Use XDocument.LoadAsync instead of XDocument.Parse --- Emby.Dlna/PlayTo/SsdpHttpClient.cs | 16 ++++++++-------- Emby.Dlna/PlayTo/TransportCommands.cs | 6 ++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 557bc69a7..b7643fb27 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -45,10 +45,10 @@ namespace Emby.Dlna.PlayTo cancellationToken) .ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - using var reader = new StreamReader(stream, Encoding.UTF8); - return XDocument.Parse( - await reader.ReadToEndAsync().ConfigureAwait(false), - LoadOptions.PreserveWhitespace); + return await XDocument.LoadAsync( + stream, + LoadOptions.PreserveWhitespace, + cancellationToken).ConfigureAwait(false); } private static string NormalizeServiceUrl(string baseUrl, string serviceUrl) @@ -94,10 +94,10 @@ namespace Emby.Dlna.PlayTo options.Headers.TryAddWithoutValidation("FriendlyName.DLNA.ORG", FriendlyName); using var response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - using var reader = new StreamReader(stream, Encoding.UTF8); - return XDocument.Parse( - await reader.ReadToEndAsync().ConfigureAwait(false), - LoadOptions.PreserveWhitespace); + return await XDocument.LoadAsync( + stream, + LoadOptions.PreserveWhitespace, + cancellationToken).ConfigureAwait(false); } private async Task PostSoapDataAsync( diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs index 0865968ad..cbcf66e45 100644 --- a/Emby.Dlna/PlayTo/TransportCommands.cs +++ b/Emby.Dlna/PlayTo/TransportCommands.cs @@ -13,12 +13,10 @@ namespace Emby.Dlna.PlayTo public class TransportCommands { private const string CommandBase = "\r\n" + "" + "" + "" + "{2}" + "" + ""; - private List _stateVariables = new List(); - private List _serviceActions = new List(); - public List StateVariables => _stateVariables; + public List StateVariables { get; } = new List(); - public List ServiceActions => _serviceActions; + public List ServiceActions { get; } = new List(); public static TransportCommands Create(XDocument document) { From 10229e1afe7e2403c59f508a667cafbb2c85a9ce Mon Sep 17 00:00:00 2001 From: Nathan Mascitelli Date: Fri, 5 Mar 2021 23:09:35 -0500 Subject: [PATCH 049/402] Link to jellyfin-ffmpeg --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5af312ad7..cba88c8d2 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Before the project can be built, you must first install the [.NET 5.0 SDK](https Instructions to run this project from the command line are included here, but you will also need to install an IDE if you want to debug the server while it is running. Any IDE that supports .NET Core development will work, but two options are recent versions of [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2017) and [Visual Studio Code](https://code.visualstudio.com/Download). -[ffmpeg](https://ffmpeg.org/download.html) will also need to be installed. +[ffmpeg](https://github.com/jellyfin/jellyfin-ffmpeg) will also need to be installed. ### Cloning the Repository From bc661c16e19413cbe6a94832280e3a24b6cf3c20 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 6 Mar 2021 14:01:37 +0100 Subject: [PATCH 050/402] simplify --- .../Library/PathExtensions.cs | 26 ++++++------------- .../Library/PathExtensionsTests.cs | 23 +++++++++------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 1fc5526ae..41e64abf3 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -103,28 +103,18 @@ namespace Emby.Server.Implementations.Library // We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results // when the sub path matches a similar but in-complete subpath - if (!subPath.EndsWith(newDirectorySeparatorChar)) + var oldSubPathEndsWithSeparator = subPath[^1] == newDirectorySeparatorChar; + if (!path.StartsWith(subPath, StringComparison.OrdinalIgnoreCase) + || (!oldSubPathEndsWithSeparator && path[subPath.Length] != newDirectorySeparatorChar)) { - subPath += newDirectorySeparatorChar; - } - - if (newSubPath.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal)) - { - newSubPath = newSubPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); - } - - if (!newSubPath.EndsWith(newDirectorySeparatorChar)) - { - newSubPath += newDirectorySeparatorChar; - } - - if (!path.Contains(subPath, StringComparison.OrdinalIgnoreCase)) - { - newPath = null; return false; } - newPath = path.Replace(subPath, newSubPath, StringComparison.OrdinalIgnoreCase); + var newSubPathTrimmed = newSubPath.AsSpan().TrimEnd(newDirectorySeparatorChar); + // Ensure that the path with the old subpath removed starts with a leading dir separator + int idx = oldSubPathEndsWithSeparator ? subPath.Length - 1 : subPath.Length; + newPath = string.Concat(newSubPathTrimmed, path.AsSpan(idx)); + return true; } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index a96a05377..a6fe90566 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -26,15 +26,16 @@ namespace Jellyfin.Server.Implementations.Tests.Library } [Theory] - [InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff", "/home/jeff", true, "/home/jeff/myfile.mkv")] - [InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff/", "/home/jeff", true, "/home/jeff/myfile.mkv")] - [InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/not jeff's band", "/home/not jeff", false, null)] - [InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/jeff's band", "/home/not jeff", true, "/home/not jeff/consistently inconsistent.mp3")] - [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff", true, "/home/jeff/myfile.mkv")] - public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, bool succeeded, string? expectedResult) + [InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff", "/home/jeff", "/home/jeff/myfile.mkv")] + [InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff/", "/home/jeff", "/home/jeff/myfile.mkv")] + [InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/jeff's band", "/home/not jeff", "/home/not jeff/consistently inconsistent.mp3")] + [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff", "/home/jeff/myfile.mkv")] + [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff/", "/home/jeff/myfile.mkv")] + [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/home/jeff/", "/home/jeff/myfile.mkv")] + [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/", "/myfile.mkv")] + public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, string? expectedResult) { - var status = PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result); - Assert.Equal(succeeded, status); + Assert.True(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result)); Assert.Equal(expectedResult, result); } @@ -43,9 +44,11 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("/my/path", "", "")] [InlineData("", "/another/path", "")] [InlineData("", "", "/new/subpath")] - public void TryReplaceSubPath_EmptyString_ThrowsArgumentNullException(string path, string subPath, string newSubPath) + [InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/not jeff's band", "/home/not jeff")] + public void TryReplaceSubPath_InvalidInput_ReturnsFalseAndNull(string path, string subPath, string newSubPath) { - Assert.Throws(() => PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out _)); + Assert.False(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result)); + Assert.Null(result); } } } From 6f898145afe013d7dfcd47fbc6adab9316e2dd63 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 6 Mar 2021 14:50:27 +0100 Subject: [PATCH 051/402] Use Uri.TryCreate and ImageType helper method --- .../Parsers/BaseNfoParser.cs | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index aa89936d0..2e222c0a4 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -803,25 +803,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } - ImageType imageType = artType switch - { - "banner" => ImageType.Banner, - "clearlogo" => ImageType.Logo, - "discart" => ImageType.Disc, - "landscape" => ImageType.Thumb, - "clearart" => ImageType.Art, - // unknown type (including "poster") --> primary - _ => ImageType.Primary, - }; + ImageType imageType = GetImageType(artType); - Uri uri; - try + if (!Uri.TryCreate(val, UriKind.Absolute, out var uri) || uri == null) { - uri = new Uri(val); - } - catch (UriFormatException ex) - { - Logger.LogError(ex, "Image location {Path} specified in nfo file for {ItemName} is not a valid URL or file path.", val, item.Name); + Logger.LogError("Image location {Path} specified in nfo file for {ItemName} is not a valid URL or file path.", val, item.Name); break; } @@ -1256,5 +1242,24 @@ namespace MediaBrowser.XbmcMetadata.Parsers return string.IsNullOrWhiteSpace(value) ? Array.Empty() : value.Split(separator, StringSplitOptions.RemoveEmptyEntries); } + + /// + /// Parses the ImageType from the nfo aspect property. + /// + /// The nfo aspect property. + /// The image type. + private static ImageType GetImageType(string aspect) + { + return aspect switch + { + "banner" => ImageType.Banner, + "clearlogo" => ImageType.Logo, + "discart" => ImageType.Disc, + "landscape" => ImageType.Thumb, + "clearart" => ImageType.Art, + // unknown type (including "poster") --> primary + _ => ImageType.Primary, + }; + } } } From b8d52dafa9047403cb7bde221baf9d39baf24e18 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Sat, 6 Mar 2021 16:02:52 +0100 Subject: [PATCH 052/402] Update MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs Co-authored-by: Bond-009 --- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 2e222c0a4..18b4ffac9 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -805,7 +805,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers ImageType imageType = GetImageType(artType); - if (!Uri.TryCreate(val, UriKind.Absolute, out var uri) || uri == null) + if (!Uri.TryCreate(val, UriKind.Absolute, out var uri)) { Logger.LogError("Image location {Path} specified in nfo file for {ItemName} is not a valid URL or file path.", val, item.Name); break; From 54211b921c86a4aa69e7390a662a48bf99011de1 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 6 Mar 2021 19:07:02 +0100 Subject: [PATCH 053/402] rider is a prick --- .../Library/PathExtensions.cs | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 41e64abf3..d9e20e19a 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Text.RegularExpressions; namespace Emby.Server.Implementations.Library @@ -60,19 +61,11 @@ namespace Emby.Server.Implementations.Library /// , or is empty. public static bool TryReplaceSubPath(this string path, string subPath, string newSubPath, [NotNullWhen(true)] out string? newPath) { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException(nameof(path)); - } + newPath = null; - if (string.IsNullOrWhiteSpace(subPath)) + if (path.Length == 0 || subPath.Length == 0 || newSubPath.Length == 0 || subPath.Length > path.Length) { - throw new ArgumentNullException(nameof(subPath)); - } - - if (string.IsNullOrWhiteSpace(newSubPath)) - { - throw new ArgumentNullException(nameof(newSubPath)); + return false; } char oldDirectorySeparatorChar; @@ -91,15 +84,8 @@ namespace Emby.Server.Implementations.Library newDirectorySeparatorChar = '\\'; } - if (path.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal)) - { - path = path.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); - } - - if (subPath.Contains(oldDirectorySeparatorChar, StringComparison.Ordinal)) - { - subPath = subPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); - } + path = path.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); + subPath = subPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); // We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results // when the sub path matches a similar but in-complete subpath From 67af30d1ff41734215db4c64f777568c647c2ad3 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 6 Mar 2021 20:53:50 +0100 Subject: [PATCH 054/402] Remove redundant checks --- Emby.Server.Implementations/Library/LibraryManager.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 973b2df8a..9a43405f4 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2783,11 +2783,6 @@ namespace Emby.Server.Implementations.Library { foreach (var pathInfo in libraryOptions.PathInfos) { - if (string.IsNullOrWhiteSpace(pathInfo.Path) || string.IsNullOrWhiteSpace(pathInfo.NetworkPath)) - { - continue; - } - if (path.TryReplaceSubPath(pathInfo.Path, pathInfo.NetworkPath, out var newPath)) { return newPath; @@ -2809,11 +2804,6 @@ namespace Emby.Server.Implementations.Library foreach (var map in _configurationManager.Configuration.PathSubstitutions) { - if (string.IsNullOrWhiteSpace(map.From)) - { - continue; - } - if (path.TryReplaceSubPath(map.From, map.To, out var newPath)) { return newPath; From 946411be8e85344ad4f33fb7ffb9a1666cf8e6be Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 6 Mar 2021 21:18:20 +0100 Subject: [PATCH 055/402] Remove redundant check --- .../Library/LibraryManager.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 9a43405f4..0957b8cf7 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2776,6 +2776,7 @@ namespace Emby.Server.Implementations.Library public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem) { + string newPath; if (ownerItem != null) { var libraryOptions = GetLibraryOptions(ownerItem); @@ -2783,7 +2784,7 @@ namespace Emby.Server.Implementations.Library { foreach (var pathInfo in libraryOptions.PathInfos) { - if (path.TryReplaceSubPath(pathInfo.Path, pathInfo.NetworkPath, out var newPath)) + if (path.TryReplaceSubPath(pathInfo.Path, pathInfo.NetworkPath, out newPath)) { return newPath; } @@ -2794,17 +2795,14 @@ namespace Emby.Server.Implementations.Library var metadataPath = _configurationManager.Configuration.MetadataPath; var metadataNetworkPath = _configurationManager.Configuration.MetadataNetworkPath; - if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath)) + if (path.TryReplaceSubPath(metadataPath, metadataNetworkPath, out newPath)) { - if (path.TryReplaceSubPath(metadataPath, metadataNetworkPath, out var newPath)) - { - return newPath; - } + return newPath; } foreach (var map in _configurationManager.Configuration.PathSubstitutions) { - if (path.TryReplaceSubPath(map.From, map.To, out var newPath)) + if (path.TryReplaceSubPath(map.From, map.To, out newPath)) { return newPath; } From 287dab4655176b09b0cd5b7607bfaff74e48d20d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Mar 2021 16:17:19 -0500 Subject: [PATCH 056/402] Remove constructor side effects and remove unneeded parameterless constructors --- Jellyfin.Data/Entities/AccessSchedule.cs | 9 ----- Jellyfin.Data/Entities/ActivityLog.cs | 13 +------ .../Entities/CustomItemDisplayPreferences.cs | 17 +++----- Jellyfin.Data/Entities/DisplayPreferences.cs | 7 ---- Jellyfin.Data/Entities/Group.cs | 10 ----- Jellyfin.Data/Entities/ImageInfo.cs | 10 ----- .../Entities/ItemDisplayPreferences.cs | 7 ---- Jellyfin.Data/Entities/Libraries/Artwork.cs | 15 +------ Jellyfin.Data/Entities/Libraries/Book.cs | 3 +- .../Entities/Libraries/BookMetadata.cs | 21 +--------- Jellyfin.Data/Entities/Libraries/Chapter.cs | 20 +--------- .../Entities/Libraries/CollectionItem.cs | 39 ------------------- Jellyfin.Data/Entities/Libraries/Company.cs | 15 +------ .../Entities/Libraries/CompanyMetadata.cs | 17 +------- .../Entities/Libraries/CustomItem.cs | 3 +- .../Entities/Libraries/CustomItemMetadata.cs | 19 +-------- Jellyfin.Data/Entities/Libraries/Episode.cs | 22 +---------- .../Entities/Libraries/EpisodeMetadata.cs | 20 +--------- Jellyfin.Data/Entities/Libraries/Genre.cs | 26 +------------ .../Entities/Libraries/ItemMetadata.cs | 10 ----- Jellyfin.Data/Entities/Libraries/Library.cs | 10 ----- .../Entities/Libraries/LibraryItem.cs | 7 ---- Jellyfin.Data/Entities/Libraries/MediaFile.cs | 20 +--------- .../Entities/Libraries/MediaFileStream.cs | 21 +--------- .../Entities/Libraries/MetadataProvider.cs | 10 ----- .../Entities/Libraries/MetadataProviderId.cs | 20 +--------- Jellyfin.Data/Entities/Libraries/Movie.cs | 3 +- .../Entities/Libraries/MovieMetadata.cs | 15 +------ .../Entities/Libraries/MusicAlbum.cs | 3 +- .../Entities/Libraries/MusicAlbumMetadata.cs | 15 +------ Jellyfin.Data/Entities/Libraries/Person.cs | 10 ----- .../Entities/Libraries/PersonRole.cs | 22 +---------- Jellyfin.Data/Entities/Libraries/Photo.cs | 3 +- .../Entities/Libraries/PhotoMetadata.cs | 21 +--------- Jellyfin.Data/Entities/Libraries/Rating.cs | 21 +--------- .../Entities/Libraries/RatingSource.cs | 21 +--------- Jellyfin.Data/Entities/Libraries/Release.cs | 15 +------ Jellyfin.Data/Entities/Libraries/Season.cs | 22 +---------- .../Entities/Libraries/SeasonMetadata.cs | 20 +--------- Jellyfin.Data/Entities/Libraries/Series.cs | 3 +- .../Entities/Libraries/SeriesMetadata.cs | 21 +--------- Jellyfin.Data/Entities/Libraries/Track.cs | 22 +---------- .../Entities/Libraries/TrackMetadata.cs | 21 +--------- Jellyfin.Data/Entities/Permission.cs | 8 ---- Jellyfin.Data/Entities/Preference.cs | 8 ---- Jellyfin.Data/Entities/User.cs | 8 ---- 46 files changed, 46 insertions(+), 627 deletions(-) diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs index 7d1b76a3f..72bca061d 100644 --- a/Jellyfin.Data/Entities/AccessSchedule.cs +++ b/Jellyfin.Data/Entities/AccessSchedule.cs @@ -1,7 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Text.Json.Serialization; using System.Xml.Serialization; using Jellyfin.Data.Enums; @@ -27,14 +26,6 @@ namespace Jellyfin.Data.Entities EndHour = endHour; } - /// - /// Initializes a new instance of the class. - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected AccessSchedule() - { - } - /// /// Gets or sets the id of this instance. /// diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index e2d5c7187..80e32db30 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -18,8 +18,7 @@ namespace Jellyfin.Data.Entities /// The name. /// The type. /// The user id. - /// The log level. - public ActivityLog(string name, string type, Guid userId, LogLevel logLevel = LogLevel.Information) + public ActivityLog(string name, string type, Guid userId) { if (string.IsNullOrEmpty(name)) { @@ -35,15 +34,7 @@ namespace Jellyfin.Data.Entities Type = type; UserId = userId; DateCreated = DateTime.UtcNow; - LogSeverity = logLevel; - } - - /// - /// Initializes a new instance of the class. - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected ActivityLog() - { + LogSeverity = LogLevel.Information; } /// diff --git a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs index 511e3b281..d407180d4 100644 --- a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs @@ -15,22 +15,15 @@ namespace Jellyfin.Data.Entities /// The user id. /// The item id. /// The client. - /// The preference key. - /// The preference value. - public CustomItemDisplayPreferences(Guid userId, Guid itemId, string client, string preferenceKey, string preferenceValue) + /// The preference key. + /// The preference value. + public CustomItemDisplayPreferences(Guid userId, Guid itemId, string client, string key, string value) { UserId = userId; ItemId = itemId; Client = client; - Key = preferenceKey; - Value = preferenceValue; - } - - /// - /// Initializes a new instance of the class. - /// - protected CustomItemDisplayPreferences() - { + Key = key; + Value = value; } /// diff --git a/Jellyfin.Data/Entities/DisplayPreferences.cs b/Jellyfin.Data/Entities/DisplayPreferences.cs index 1a8ca1da3..d186deb29 100644 --- a/Jellyfin.Data/Entities/DisplayPreferences.cs +++ b/Jellyfin.Data/Entities/DisplayPreferences.cs @@ -36,13 +36,6 @@ namespace Jellyfin.Data.Entities HomeSections = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - protected DisplayPreferences() - { - } - /// /// Gets or sets the Id. /// diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index 878811e59..8c45dde92 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -32,16 +32,6 @@ namespace Jellyfin.Data.Entities Preferences = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Group() - { - } - /// /// Gets or sets the id of this group. /// diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index ab8452e62..f9ae1a955 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -19,16 +19,6 @@ namespace Jellyfin.Data.Entities LastModified = DateTime.UtcNow; } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected ImageInfo() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs index 2b25bb25f..f0a04f8ea 100644 --- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs @@ -28,13 +28,6 @@ namespace Jellyfin.Data.Entities RememberIndexing = false; } - /// - /// Initializes a new instance of the class. - /// - protected ItemDisplayPreferences() - { - } - /// /// Gets or sets the Id. /// diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs index 06cd33330..df28ce737 100644 --- a/Jellyfin.Data/Entities/Libraries/Artwork.cs +++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs @@ -18,8 +18,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The path. /// The kind of art. - /// The owner. - public Artwork(string path, ArtKind kind, IHasArtwork owner) + public Artwork(string path, ArtKind kind) { if (string.IsNullOrEmpty(path)) { @@ -28,18 +27,6 @@ namespace Jellyfin.Data.Entities.Libraries Path = path; Kind = kind; - - owner?.Artwork.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Artwork() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/Book.cs b/Jellyfin.Data/Entities/Libraries/Book.cs index 2e63f75bd..aea3d58d5 100644 --- a/Jellyfin.Data/Entities/Libraries/Book.cs +++ b/Jellyfin.Data/Entities/Libraries/Book.cs @@ -13,7 +13,8 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - public Book() + /// The library. + public Book(Library library) : base(library) { BookMetadata = new HashSet(); Releases = new HashSet(); diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs index 4a3d290f0..8b0c96530 100644 --- a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs @@ -1,6 +1,5 @@ #pragma warning disable CA2227 -using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -17,29 +16,11 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - /// The book. - public BookMetadata(string title, string language, Book book) : base(title, language) + public BookMetadata(string title, string language) : base(title, language) { - if (book == null) - { - throw new ArgumentNullException(nameof(book)); - } - - book.BookMetadata.Add(this); - Publishers = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected BookMetadata() - { - } - /// /// Gets or sets the ISBN. /// diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs index f503de379..f253143d7 100644 --- a/Jellyfin.Data/Entities/Libraries/Chapter.cs +++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs @@ -17,8 +17,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// ISO-639-3 3-character language codes. /// The start time for this chapter. - /// The release. - public Chapter(string language, long startTime, Release release) + public Chapter(string language, long startTime) { if (string.IsNullOrEmpty(language)) { @@ -27,23 +26,6 @@ namespace Jellyfin.Data.Entities.Libraries Language = language; StartTime = startTime; - - if (release == null) - { - throw new ArgumentNullException(nameof(release)); - } - - release.Chapters.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Chapter() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs index f9539964d..1157de442 100644 --- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -10,44 +9,6 @@ namespace Jellyfin.Data.Entities.Libraries /// public class CollectionItem : IHasConcurrencyToken { - /// - /// Initializes a new instance of the class. - /// - /// The collection. - /// The previous item. - /// The next item. - public CollectionItem(Collection collection, CollectionItem previous, CollectionItem next) - { - if (collection == null) - { - throw new ArgumentNullException(nameof(collection)); - } - - collection.Items.Add(this); - - if (next != null) - { - Next = next; - next.Previous = this; - } - - if (previous != null) - { - Previous = previous; - previous.Next = this; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected CollectionItem() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs index 3b6ed3309..499ba3800 100644 --- a/Jellyfin.Data/Entities/Libraries/Company.cs +++ b/Jellyfin.Data/Entities/Libraries/Company.cs @@ -15,24 +15,11 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - /// The owner of this company. - public Company(IHasCompanies owner) + public Company() { - owner?.Companies.Add(this); - CompanyMetadata = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Company() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs index 8aa0486af..86642b38a 100644 --- a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; namespace Jellyfin.Data.Entities.Libraries @@ -13,21 +12,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - /// The company. - public CompanyMetadata(string title, string language, Company company) : base(title, language) - { - if (company == null) - { - throw new ArgumentNullException(nameof(company)); - } - - company.CompanyMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - protected CompanyMetadata() + public CompanyMetadata(string title, string language) : base(title, language) { } diff --git a/Jellyfin.Data/Entities/Libraries/CustomItem.cs b/Jellyfin.Data/Entities/Libraries/CustomItem.cs index 115489c78..88d1a0c25 100644 --- a/Jellyfin.Data/Entities/Libraries/CustomItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CustomItem.cs @@ -13,7 +13,8 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - public CustomItem() + /// The library. + public CustomItem(Library library) : base(library) { CustomItemMetadata = new HashSet(); Releases = new HashSet(); diff --git a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs index f0daedfbe..a69a8eafa 100644 --- a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs @@ -12,24 +12,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - /// The item. - public CustomItemMetadata(string title, string language, CustomItem item) : base(title, language) - { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - item.CustomItemMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected CustomItemMetadata() + public CustomItemMetadata(string title, string language) : base(title, language) { } } diff --git a/Jellyfin.Data/Entities/Libraries/Episode.cs b/Jellyfin.Data/Entities/Libraries/Episode.cs index 0bdc2d764..458c7d9f5 100644 --- a/Jellyfin.Data/Entities/Libraries/Episode.cs +++ b/Jellyfin.Data/Entities/Libraries/Episode.cs @@ -1,6 +1,5 @@ #pragma warning disable CA2227 -using System; using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -14,30 +13,13 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - /// The season. - public Episode(Season season) + /// The library. + public Episode(Library library) : base(library) { - if (season == null) - { - throw new ArgumentNullException(nameof(season)); - } - - season.Episodes.Add(this); - Releases = new HashSet(); EpisodeMetadata = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Episode() - { - } - /// /// Gets or sets the episode number. /// diff --git a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs index 7efb840f0..5662decdb 100644 --- a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; namespace Jellyfin.Data.Entities.Libraries @@ -13,24 +12,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - /// The episode. - public EpisodeMetadata(string title, string language, Episode episode) : base(title, language) - { - if (episode == null) - { - throw new ArgumentNullException(nameof(episode)); - } - - episode.EpisodeMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected EpisodeMetadata() + public EpisodeMetadata(string title, string language) : base(title, language) { } diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs index 2a2dbd1a5..befa75550 100644 --- a/Jellyfin.Data/Entities/Libraries/Genre.cs +++ b/Jellyfin.Data/Entities/Libraries/Genre.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -14,32 +13,9 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The name. - /// The metadata. - public Genre(string name, ItemMetadata itemMetadata) + public Genre(string name) { - if (string.IsNullOrEmpty(name)) - { - throw new ArgumentNullException(nameof(name)); - } - Name = name; - - if (itemMetadata == null) - { - throw new ArgumentNullException(nameof(itemMetadata)); - } - - itemMetadata.Genres.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Genre() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs index d74330c05..a0efa66e4 100644 --- a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs @@ -42,16 +42,6 @@ namespace Jellyfin.Data.Entities.Libraries Sources = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to being abstract. - /// - protected ItemMetadata() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/Library.cs b/Jellyfin.Data/Entities/Libraries/Library.cs index 4f82a2e2a..3ec4341a4 100644 --- a/Jellyfin.Data/Entities/Libraries/Library.cs +++ b/Jellyfin.Data/Entities/Libraries/Library.cs @@ -24,16 +24,6 @@ namespace Jellyfin.Data.Entities.Libraries Name = name; } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Library() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs index a9167aa7f..504b9c853 100644 --- a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs +++ b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs @@ -20,13 +20,6 @@ namespace Jellyfin.Data.Entities.Libraries Library = library; } - /// - /// Initializes a new instance of the class. - /// - protected LibraryItem() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs index 9924d5728..7f64978e2 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFile.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs @@ -19,8 +19,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The path relative to the LibraryRoot. /// The file kind. - /// The release. - public MediaFile(string path, MediaFileKind kind, Release release) + public MediaFile(string path, MediaFileKind kind) { if (string.IsNullOrEmpty(path)) { @@ -30,26 +29,9 @@ namespace Jellyfin.Data.Entities.Libraries Path = path; Kind = kind; - if (release == null) - { - throw new ArgumentNullException(nameof(release)); - } - - release.MediaFiles.Add(this); - MediaFileStreams = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MediaFile() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs index 5b03e260e..c4468766f 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -14,27 +13,9 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The number of this stream. - /// The media file. - public MediaFileStream(int streamNumber, MediaFile mediaFile) + public MediaFileStream(int streamNumber) { StreamNumber = streamNumber; - - if (mediaFile == null) - { - throw new ArgumentNullException(nameof(mediaFile)); - } - - mediaFile.MediaFileStreams.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MediaFileStream() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs index a18a612bc..20de5bf4b 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs @@ -24,16 +24,6 @@ namespace Jellyfin.Data.Entities.Libraries Name = name; } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MetadataProvider() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs index fcfb35bfa..12672dd25 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs @@ -14,8 +14,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The provider id. - /// The metadata entity. - public MetadataProviderId(string providerId, ItemMetadata itemMetadata) + public MetadataProviderId(string providerId) { if (string.IsNullOrEmpty(providerId)) { @@ -23,23 +22,6 @@ namespace Jellyfin.Data.Entities.Libraries } ProviderId = providerId; - - if (itemMetadata == null) - { - throw new ArgumentNullException(nameof(itemMetadata)); - } - - itemMetadata.Sources.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MetadataProviderId() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/Movie.cs b/Jellyfin.Data/Entities/Libraries/Movie.cs index 08db904fa..f89cacff4 100644 --- a/Jellyfin.Data/Entities/Libraries/Movie.cs +++ b/Jellyfin.Data/Entities/Libraries/Movie.cs @@ -13,7 +13,8 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - public Movie() + /// The library. + public Movie(Library library) : base(library) { Releases = new HashSet(); MovieMetadata = new HashSet(); diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs index aa1501a5c..8cf7ca6a7 100644 --- a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs @@ -17,22 +17,9 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the movie. /// ISO-639-3 3-character language codes. - /// The movie. - public MovieMetadata(string title, string language, Movie movie) : base(title, language) + public MovieMetadata(string title, string language) : base(title, language) { Studios = new HashSet(); - - movie.MovieMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MovieMetadata() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs index 06aff6f45..4049cdac8 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs @@ -12,7 +12,8 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - public MusicAlbum() + /// The library. + public MusicAlbum(Library library) : base(library) { MusicAlbumMetadata = new HashSet(); Tracks = new HashSet(); diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs index 05c0b0374..9e44e550a 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs @@ -15,22 +15,9 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the album. /// ISO-639-3 3-character language codes. - /// The music album. - public MusicAlbumMetadata(string title, string language, MusicAlbum album) : base(title, language) + public MusicAlbumMetadata(string title, string language) : base(title, language) { Labels = new HashSet(); - - album.MusicAlbumMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MusicAlbumMetadata() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs index af4c87b73..cc4b9e0f9 100644 --- a/Jellyfin.Data/Entities/Libraries/Person.cs +++ b/Jellyfin.Data/Entities/Libraries/Person.cs @@ -31,16 +31,6 @@ namespace Jellyfin.Data.Entities.Libraries Sources = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Person() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs index cd38ee83d..3ae2e4a68 100644 --- a/Jellyfin.Data/Entities/Libraries/PersonRole.cs +++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs @@ -1,6 +1,5 @@ #pragma warning disable CA2227 -using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -18,31 +17,12 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The role type. - /// The metadata. - public PersonRole(PersonRoleType type, ItemMetadata itemMetadata) + public PersonRole(PersonRoleType type) { Type = type; - - if (itemMetadata == null) - { - throw new ArgumentNullException(nameof(itemMetadata)); - } - - itemMetadata.PersonRoles.Add(this); - Sources = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected PersonRole() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/Photo.cs b/Jellyfin.Data/Entities/Libraries/Photo.cs index 25562ec96..eb5c96267 100644 --- a/Jellyfin.Data/Entities/Libraries/Photo.cs +++ b/Jellyfin.Data/Entities/Libraries/Photo.cs @@ -13,7 +13,8 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - public Photo() + /// The library. + public Photo(Library library) : base(library) { PhotoMetadata = new HashSet(); Releases = new HashSet(); diff --git a/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs index ffc790b57..6c284307d 100644 --- a/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs @@ -1,5 +1,3 @@ -using System; - namespace Jellyfin.Data.Entities.Libraries { /// @@ -12,24 +10,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the photo. /// ISO-639-3 3-character language codes. - /// The photo. - public PhotoMetadata(string title, string language, Photo photo) : base(title, language) - { - if (photo == null) - { - throw new ArgumentNullException(nameof(photo)); - } - - photo.PhotoMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected PhotoMetadata() + public PhotoMetadata(string title, string language) : base(title, language) { } } diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs index 98226cd80..0ea933fd7 100644 --- a/Jellyfin.Data/Entities/Libraries/Rating.cs +++ b/Jellyfin.Data/Entities/Libraries/Rating.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -14,27 +13,9 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The value. - /// The metadata. - public Rating(double value, ItemMetadata itemMetadata) + public Rating(double value) { Value = value; - - if (itemMetadata == null) - { - throw new ArgumentNullException(nameof(itemMetadata)); - } - - itemMetadata.Ratings.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Rating() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/RatingSource.cs b/Jellyfin.Data/Entities/Libraries/RatingSource.cs index 549f41804..7e1a5a8f4 100644 --- a/Jellyfin.Data/Entities/Libraries/RatingSource.cs +++ b/Jellyfin.Data/Entities/Libraries/RatingSource.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -15,28 +14,10 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The minimum value. /// The maximum value. - /// The rating. - public RatingSource(double minimumValue, double maximumValue, Rating rating) + public RatingSource(double minimumValue, double maximumValue) { MinimumValue = minimumValue; MaximumValue = maximumValue; - - if (rating == null) - { - throw new ArgumentNullException(nameof(rating)); - } - - rating.RatingType = this; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected RatingSource() - { } /// diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs index b633e08fb..1871e0f10 100644 --- a/Jellyfin.Data/Entities/Libraries/Release.cs +++ b/Jellyfin.Data/Entities/Libraries/Release.cs @@ -17,8 +17,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The name of this release. - /// The owner of this release. - public Release(string name, IHasReleases owner) + public Release(string name) { if (string.IsNullOrEmpty(name)) { @@ -27,22 +26,10 @@ namespace Jellyfin.Data.Entities.Libraries Name = name; - owner?.Releases.Add(this); - MediaFiles = new HashSet(); Chapters = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Release() - { - } - /// /// Gets or sets the id. /// diff --git a/Jellyfin.Data/Entities/Libraries/Season.cs b/Jellyfin.Data/Entities/Libraries/Season.cs index eb6674dbc..04f723a1d 100644 --- a/Jellyfin.Data/Entities/Libraries/Season.cs +++ b/Jellyfin.Data/Entities/Libraries/Season.cs @@ -1,6 +1,5 @@ #pragma warning disable CA2227 -using System; using System.Collections.Generic; namespace Jellyfin.Data.Entities.Libraries @@ -13,30 +12,13 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - /// The series. - public Season(Series series) + /// The library. + public Season(Library library) : base(library) { - if (series == null) - { - throw new ArgumentNullException(nameof(series)); - } - - series.Seasons.Add(this); - Episodes = new HashSet(); SeasonMetadata = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Season() - { - } - /// /// Gets or sets the season number. /// diff --git a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs index 7ce79756b..61714f909 100644 --- a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; namespace Jellyfin.Data.Entities.Libraries @@ -13,24 +12,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - /// The season. - public SeasonMetadata(string title, string language, Season season) : base(title, language) - { - if (season == null) - { - throw new ArgumentNullException(nameof(season)); - } - - season.SeasonMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected SeasonMetadata() + public SeasonMetadata(string title, string language) : base(title, language) { } diff --git a/Jellyfin.Data/Entities/Libraries/Series.cs b/Jellyfin.Data/Entities/Libraries/Series.cs index 8c8317d14..59508831e 100644 --- a/Jellyfin.Data/Entities/Libraries/Series.cs +++ b/Jellyfin.Data/Entities/Libraries/Series.cs @@ -13,7 +13,8 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - public Series() + /// The library. + public Series(Library library) : base(library) { DateAdded = DateTime.UtcNow; Seasons = new HashSet(); diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs index 877dbfc69..e1acd2d45 100644 --- a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs @@ -1,6 +1,5 @@ #pragma warning disable CA2227 -using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -18,29 +17,11 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - /// The series. - public SeriesMetadata(string title, string language, Series series) : base(title, language) + public SeriesMetadata(string title, string language) : base(title, language) { - if (series == null) - { - throw new ArgumentNullException(nameof(series)); - } - - series.SeriesMetadata.Add(this); - Networks = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected SeriesMetadata() - { - } - /// /// Gets or sets the outline. /// diff --git a/Jellyfin.Data/Entities/Libraries/Track.cs b/Jellyfin.Data/Entities/Libraries/Track.cs index 782bfb5ce..86a3edff8 100644 --- a/Jellyfin.Data/Entities/Libraries/Track.cs +++ b/Jellyfin.Data/Entities/Libraries/Track.cs @@ -1,6 +1,5 @@ #pragma warning disable CA2227 -using System; using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -14,30 +13,13 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Initializes a new instance of the class. /// - /// The album. - public Track(MusicAlbum album) + /// The library. + public Track(Library library) : base(library) { - if (album == null) - { - throw new ArgumentNullException(nameof(album)); - } - - album.Tracks.Add(this); - Releases = new HashSet(); TrackMetadata = new HashSet(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Track() - { - } - /// /// Gets or sets the track number. /// diff --git a/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs index 321f93bf2..042d2b90d 100644 --- a/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs @@ -1,5 +1,3 @@ -using System; - namespace Jellyfin.Data.Entities.Libraries { /// @@ -12,24 +10,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - /// The track. - public TrackMetadata(string title, string language, Track track) : base(title, language) - { - if (track == null) - { - throw new ArgumentNullException(nameof(track)); - } - - track.TrackMetadata.Add(this); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected TrackMetadata() + public TrackMetadata(string title, string language) : base(title, language) { } } diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index d92e5d9d2..17e3bf50e 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -22,14 +22,6 @@ namespace Jellyfin.Data.Entities Value = value; } - /// - /// Initializes a new instance of the class. - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Permission() - { - } - /// /// Gets or sets the id of this permission. /// diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 4efddf2a4..40f2f8ede 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -23,14 +23,6 @@ namespace Jellyfin.Data.Entities Value = value ?? throw new ArgumentNullException(nameof(value)); } - /// - /// Initializes a new instance of the class. - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Preference() - { - } - /// /// Gets or sets the id of this preference. /// diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 362f3b4eb..28e12adde 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -77,14 +77,6 @@ namespace Jellyfin.Data.Entities AddDefaultPreferences(); } - /// - /// Initializes a new instance of the class. - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected User() - { - } - /// /// Gets or sets the Id of the user. /// From f638ee6b0918c2ef05ec11cfa43584d3efad68d1 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Mar 2021 17:43:01 -0500 Subject: [PATCH 057/402] Enable nullable for Jellyfin.Data and remove unnecessary attributes --- Emby.Drawing/ImageProcessor.cs | 7 ++++++- Jellyfin.Api/Controllers/ImageController.cs | 12 +++++++++++- Jellyfin.Api/Controllers/StartupController.cs | 5 ++++- Jellyfin.Data/Entities/AccessSchedule.cs | 7 ------- Jellyfin.Data/Entities/ActivityLog.cs | 8 +++----- .../Entities/CustomItemDisplayPreferences.cs | 3 --- Jellyfin.Data/Entities/DisplayPreferences.cs | 7 ++----- Jellyfin.Data/Entities/Group.cs | 1 - Jellyfin.Data/Entities/HomeSection.cs | 1 - Jellyfin.Data/Entities/ImageInfo.cs | 1 - .../Entities/ItemDisplayPreferences.cs | 2 -- Jellyfin.Data/Entities/Libraries/Artwork.cs | 1 - .../Entities/Libraries/BookMetadata.cs | 2 -- Jellyfin.Data/Entities/Libraries/Chapter.cs | 3 +-- Jellyfin.Data/Entities/Libraries/Collection.cs | 2 +- .../Entities/Libraries/CollectionItem.cs | 13 +++++++++++-- Jellyfin.Data/Entities/Libraries/Company.cs | 2 +- .../Entities/Libraries/CompanyMetadata.cs | 8 ++++---- .../Entities/Libraries/CustomItemMetadata.cs | 2 -- .../Entities/Libraries/EpisodeMetadata.cs | 6 +++--- Jellyfin.Data/Entities/Libraries/Genre.cs | 1 - .../Entities/Libraries/ItemMetadata.cs | 6 ++---- Jellyfin.Data/Entities/Libraries/Library.cs | 12 +++--------- Jellyfin.Data/Entities/Libraries/LibraryItem.cs | 1 - Jellyfin.Data/Entities/Libraries/MediaFile.cs | 1 - .../Entities/Libraries/MetadataProvider.cs | 1 - .../Entities/Libraries/MetadataProviderId.cs | 5 +++-- .../Entities/Libraries/MovieMetadata.cs | 10 ++++------ .../Entities/Libraries/MusicAlbumMetadata.cs | 6 +++--- Jellyfin.Data/Entities/Libraries/Person.cs | 3 +-- Jellyfin.Data/Entities/Libraries/PersonRole.cs | 8 +++++--- Jellyfin.Data/Entities/Libraries/Rating.cs | 2 +- .../Entities/Libraries/RatingSource.cs | 4 ++-- Jellyfin.Data/Entities/Libraries/Release.cs | 1 - .../Entities/Libraries/SeasonMetadata.cs | 2 +- .../Entities/Libraries/SeriesMetadata.cs | 9 ++++----- Jellyfin.Data/Entities/Preference.cs | 1 - Jellyfin.Data/Entities/User.cs | 17 +++++++---------- Jellyfin.Data/Jellyfin.Data.csproj | 1 + .../Consumers/Session/PlaybackStartLogger.cs | 4 ++-- .../Users/UserManager.cs | 6 +++--- .../Migrations/Routines/MigrateUserDb.cs | 4 +--- .../Drawing/IImageProcessor.cs | 2 +- tests/Jellyfin.Api.Tests/TestHelpers.cs | 4 ++-- 44 files changed, 93 insertions(+), 111 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 8a2301d2d..aa8a3d212 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -352,8 +352,13 @@ namespace Emby.Drawing } /// - public string GetImageCacheTag(User user) + public string? GetImageCacheTag(User user) { + if (user.ProfileImage == null) + { + return null; + } + return (user.ProfileImage.Path + user.ProfileImage.LastModified.Ticks).GetMD5() .ToString("N", CultureInfo.InvariantCulture); } diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index a50d6e46b..b016f2450 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -196,6 +196,11 @@ namespace Jellyfin.Api.Controllers } var user = _userManager.GetUserById(userId); + if (user?.ProfileImage == null) + { + return NoContent(); + } + try { System.IO.File.Delete(user.ProfileImage.Path); @@ -235,6 +240,11 @@ namespace Jellyfin.Api.Controllers } var user = _userManager.GetUserById(userId); + if (user?.ProfileImage == null) + { + return NoContent(); + } + try { System.IO.File.Delete(user.ProfileImage.Path); @@ -1469,7 +1479,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? imageIndex) { var user = _userManager.GetUserById(userId); - if (user == null) + if (user?.ProfileImage == null) { return NotFound(); } diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index d9cb34557..a01a617fc 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -132,7 +132,10 @@ namespace Jellyfin.Api.Controllers { var user = _userManager.Users.First(); - user.Username = startupUserDto.Name; + if (startupUserDto.Name != null) + { + user.Username = startupUserDto.Name; + } await _userManager.UpdateUserAsync(user).ConfigureAwait(false); diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs index 72bca061d..9ac0666ac 100644 --- a/Jellyfin.Data/Entities/AccessSchedule.cs +++ b/Jellyfin.Data/Entities/AccessSchedule.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; using Jellyfin.Data.Enums; @@ -33,8 +32,6 @@ namespace Jellyfin.Data.Entities /// Identity, Indexed, Required. /// [XmlIgnore] - [Key] - [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } @@ -42,28 +39,24 @@ namespace Jellyfin.Data.Entities /// Gets or sets the id of the associated user. /// [XmlIgnore] - [Required] public Guid UserId { get; protected set; } /// /// Gets or sets the day of week. /// /// The day of week. - [Required] public DynamicDayOfWeek DayOfWeek { get; set; } /// /// Gets or sets the start hour. /// /// The start hour. - [Required] public double StartHour { get; set; } /// /// Gets or sets the end hour. /// /// The end hour. - [Required] public double EndHour { get; set; } /// diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index 80e32db30..e4534e8b5 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -50,7 +50,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 512. /// - [Required] [MaxLength(512)] [StringLength(512)] public string Name { get; set; } @@ -63,7 +62,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(512)] [StringLength(512)] - public string Overview { get; set; } + public string? Overview { get; set; } /// /// Gets or sets the short overview. @@ -73,7 +72,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(512)] [StringLength(512)] - public string ShortOverview { get; set; } + public string? ShortOverview { get; set; } /// /// Gets or sets the type. @@ -81,7 +80,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 256. /// - [Required] [MaxLength(256)] [StringLength(256)] public string Type { get; set; } @@ -102,7 +100,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(256)] [StringLength(256)] - public string ItemId { get; set; } + public string? ItemId { get; set; } /// /// Gets or sets the date created. This should be in UTC. diff --git a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs index d407180d4..cc46248c7 100644 --- a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs @@ -57,7 +57,6 @@ namespace Jellyfin.Data.Entities /// /// Required. Max Length = 32. /// - [Required] [MaxLength(32)] [StringLength(32)] public string Client { get; set; } @@ -68,7 +67,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public string Key { get; set; } /// @@ -77,7 +75,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public string Value { get; set; } } } diff --git a/Jellyfin.Data/Entities/DisplayPreferences.cs b/Jellyfin.Data/Entities/DisplayPreferences.cs index d186deb29..64cd6812a 100644 --- a/Jellyfin.Data/Entities/DisplayPreferences.cs +++ b/Jellyfin.Data/Entities/DisplayPreferences.cs @@ -30,8 +30,6 @@ namespace Jellyfin.Data.Entities SkipBackwardLength = 10000; ScrollDirection = ScrollDirection.Horizontal; ChromecastVersion = ChromecastVersion.Stable; - DashboardTheme = string.Empty; - TvHome = string.Empty; HomeSections = new HashSet(); } @@ -67,7 +65,6 @@ namespace Jellyfin.Data.Entities /// /// Required. Max Length = 32. /// - [Required] [MaxLength(32)] [StringLength(32)] public string Client { get; set; } @@ -138,14 +135,14 @@ namespace Jellyfin.Data.Entities /// [MaxLength(32)] [StringLength(32)] - public string DashboardTheme { get; set; } + public string? DashboardTheme { get; set; } /// /// Gets or sets the tv home screen. /// [MaxLength(32)] [StringLength(32)] - public string TvHome { get; set; } + public string? TvHome { get; set; } /// /// Gets or sets the home sections. diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index 8c45dde92..b14e22b7b 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -46,7 +46,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/HomeSection.cs b/Jellyfin.Data/Entities/HomeSection.cs index 062046260..5adc52491 100644 --- a/Jellyfin.Data/Entities/HomeSection.cs +++ b/Jellyfin.Data/Entities/HomeSection.cs @@ -15,7 +15,6 @@ namespace Jellyfin.Data.Entities /// /// Identity. Required. /// - [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index f9ae1a955..e0c37047d 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -39,7 +39,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] [MaxLength(512)] [StringLength(512)] public string Path { get; set; } diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs index f0a04f8ea..4bfeb2fa3 100644 --- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs @@ -59,7 +59,6 @@ namespace Jellyfin.Data.Entities /// /// Required. Max Length = 32. /// - [Required] [MaxLength(32)] [StringLength(32)] public string Client { get; set; } @@ -99,7 +98,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] [MaxLength(64)] [StringLength(64)] public string SortBy { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs index df28ce737..84a524de2 100644 --- a/Jellyfin.Data/Entities/Libraries/Artwork.cs +++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs @@ -44,7 +44,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 65535. /// - [Required] [MaxLength(65535)] [StringLength(65535)] public string Path { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs index 8b0c96530..1ff4327b0 100644 --- a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs @@ -1,7 +1,6 @@ #pragma warning disable CA2227 using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; namespace Jellyfin.Data.Entities.Libraries @@ -32,7 +31,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection Publishers { get; protected set; } /// - [NotMapped] public ICollection Companies => Publishers; } } diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs index f253143d7..11f53ae20 100644 --- a/Jellyfin.Data/Entities/Libraries/Chapter.cs +++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs @@ -45,7 +45,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Name { get; set; } + public string? Name { get; set; } /// /// Gets or sets the language. @@ -54,7 +54,6 @@ namespace Jellyfin.Data.Entities.Libraries /// Required, Min length = 3, Max length = 3 /// ISO-639-3 3-character language codes. /// - [Required] [MinLength(3)] [MaxLength(3)] [StringLength(3)] diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs index 39eded752..d230eeb2f 100644 --- a/Jellyfin.Data/Entities/Libraries/Collection.cs +++ b/Jellyfin.Data/Entities/Libraries/Collection.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Name { get; set; } + public string? Name { get; set; } /// [ConcurrencyCheck] diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs index 1157de442..e19362bdf 100644 --- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs @@ -9,6 +9,15 @@ namespace Jellyfin.Data.Entities.Libraries /// public class CollectionItem : IHasConcurrencyToken { + /// + /// Initializes a new instance of the class. + /// + /// The library item. + public CollectionItem(LibraryItem libraryItem) + { + LibraryItem = libraryItem; + } + /// /// Gets or sets the id. /// @@ -36,7 +45,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// TODO check if this properly updated Dependant and has the proper principal relationship. /// - public virtual CollectionItem Next { get; set; } + public virtual CollectionItem? Next { get; set; } /// /// Gets or sets the previous item in the collection. @@ -44,7 +53,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// TODO check if this properly updated Dependant and has the proper principal relationship. /// - public virtual CollectionItem Previous { get; set; } + public virtual CollectionItem? Previous { get; set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs index 499ba3800..09050bb52 100644 --- a/Jellyfin.Data/Entities/Libraries/Company.cs +++ b/Jellyfin.Data/Entities/Libraries/Company.cs @@ -18,6 +18,7 @@ namespace Jellyfin.Data.Entities.Libraries public Company() { CompanyMetadata = new HashSet(); + ChildCompanies = new HashSet(); } /// @@ -44,7 +45,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection ChildCompanies { get; protected set; } /// - [NotMapped] public ICollection Companies => ChildCompanies; /// diff --git a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs index 86642b38a..a29f08c7f 100644 --- a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs @@ -24,7 +24,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Description { get; set; } + public string? Description { get; set; } /// /// Gets or sets the headquarters. @@ -34,7 +34,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(255)] [StringLength(255)] - public string Headquarters { get; set; } + public string? Headquarters { get; set; } /// /// Gets or sets the country code. @@ -44,7 +44,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets the homepage. @@ -54,6 +54,6 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Homepage { get; set; } + public string? Homepage { get; set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs index a69a8eafa..af2393870 100644 --- a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs @@ -1,5 +1,3 @@ -using System; - namespace Jellyfin.Data.Entities.Libraries { /// diff --git a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs index 5662decdb..b0ef11e0f 100644 --- a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs @@ -24,7 +24,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } /// /// Gets or sets the plot. @@ -34,7 +34,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Plot { get; set; } + public string? Plot { get; set; } /// /// Gets or sets the tagline. @@ -44,6 +44,6 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Tagline { get; set; } + public string? Tagline { get; set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs index befa75550..9f3d65028 100644 --- a/Jellyfin.Data/Entities/Libraries/Genre.cs +++ b/Jellyfin.Data/Entities/Libraries/Genre.cs @@ -33,7 +33,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Indexed, Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs index a0efa66e4..d12e011a8 100644 --- a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs @@ -57,7 +57,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Title { get; set; } @@ -70,7 +69,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string OriginalTitle { get; set; } + public string? OriginalTitle { get; set; } /// /// Gets or sets the sort title. @@ -80,7 +79,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string SortTitle { get; set; } + public string? SortTitle { get; set; } /// /// Gets or sets the language. @@ -89,7 +88,6 @@ namespace Jellyfin.Data.Entities.Libraries /// Required, Min length = 3, Max length = 3. /// ISO-639-3 3-character language codes. /// - [Required] [MinLength(3)] [MaxLength(3)] [StringLength(3)] diff --git a/Jellyfin.Data/Entities/Libraries/Library.cs b/Jellyfin.Data/Entities/Libraries/Library.cs index 3ec4341a4..e45384902 100644 --- a/Jellyfin.Data/Entities/Libraries/Library.cs +++ b/Jellyfin.Data/Entities/Libraries/Library.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -14,14 +13,11 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The name of the library. - public Library(string name) + /// The path of the library. + public Library(string name, string path) { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException(nameof(name)); - } - Name = name; + Path = path; } /// @@ -39,7 +35,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 128. /// - [Required] [MaxLength(128)] [StringLength(128)] public string Name { get; set; } @@ -50,7 +45,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required. /// - [Required] public string Path { get; set; } /// diff --git a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs index 504b9c853..67ffad944 100644 --- a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs +++ b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs @@ -44,7 +44,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required. /// - [Required] public virtual Library Library { get; set; } /// diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs index 7f64978e2..f3e2fe653 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFile.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs @@ -47,7 +47,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 65535. /// - [Required] [MaxLength(65535)] [StringLength(65535)] public string Path { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs index 20de5bf4b..fb2587882 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs @@ -39,7 +39,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs index 12672dd25..2a9c904c8 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs @@ -14,7 +14,8 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The provider id. - public MetadataProviderId(string providerId) + /// The metadata provider. + public MetadataProviderId(string providerId, MetadataProvider metadataProvider) { if (string.IsNullOrEmpty(providerId)) { @@ -22,6 +23,7 @@ namespace Jellyfin.Data.Entities.Libraries } ProviderId = providerId; + MetadataProvider = metadataProvider; } /// @@ -39,7 +41,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string ProviderId { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs index 8cf7ca6a7..fb181dea6 100644 --- a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; namespace Jellyfin.Data.Entities.Libraries @@ -30,7 +29,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } /// /// Gets or sets the tagline. @@ -40,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Tagline { get; set; } + public string? Tagline { get; set; } /// /// Gets or sets the plot. @@ -50,7 +49,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Plot { get; set; } + public string? Plot { get; set; } /// /// Gets or sets the country code. @@ -60,7 +59,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets the studios that produced this movie. @@ -68,7 +67,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection Studios { get; protected set; } /// - [NotMapped] public ICollection Companies => Studios; } } diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs index 9e44e550a..3080bd692 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs @@ -28,7 +28,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(255)] [StringLength(255)] - public string Barcode { get; set; } + public string? Barcode { get; set; } /// /// Gets or sets the label number. @@ -38,7 +38,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(255)] [StringLength(255)] - public string LabelNumber { get; set; } + public string? LabelNumber { get; set; } /// /// Gets or sets the country code. @@ -48,7 +48,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets a collection containing the labels. diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs index cc4b9e0f9..159bd47be 100644 --- a/Jellyfin.Data/Entities/Libraries/Person.cs +++ b/Jellyfin.Data/Entities/Libraries/Person.cs @@ -46,7 +46,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Name { get; set; } @@ -59,7 +58,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(256)] [StringLength(256)] - public string SourceId { get; set; } + public string? SourceId { get; set; } /// /// Gets or sets the date added. diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs index 3ae2e4a68..988aa84ba 100644 --- a/Jellyfin.Data/Entities/Libraries/PersonRole.cs +++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs @@ -17,9 +17,12 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The role type. - public PersonRole(PersonRoleType type) + /// The person. + public PersonRole(PersonRoleType type, Person person) { Type = type; + Person = person; + Artwork = new HashSet(); Sources = new HashSet(); } @@ -40,7 +43,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Role { get; set; } + public string? Role { get; set; } /// /// Gets or sets the person's role type. @@ -60,7 +63,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required. /// - [Required] public virtual Person Person { get; set; } /// diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs index 0ea933fd7..6862012a8 100644 --- a/Jellyfin.Data/Entities/Libraries/Rating.cs +++ b/Jellyfin.Data/Entities/Libraries/Rating.cs @@ -48,7 +48,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the rating type. /// If this is null it's the internal user rating. /// - public virtual RatingSource RatingType { get; set; } + public virtual RatingSource? RatingType { get; set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/RatingSource.cs b/Jellyfin.Data/Entities/Libraries/RatingSource.cs index 7e1a5a8f4..ae0d806ff 100644 --- a/Jellyfin.Data/Entities/Libraries/RatingSource.cs +++ b/Jellyfin.Data/Entities/Libraries/RatingSource.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Name { get; set; } + public string? Name { get; set; } /// /// Gets or sets the minimum value. @@ -62,7 +62,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Gets or sets the metadata source. /// - public virtual MetadataProviderId Source { get; set; } + public virtual MetadataProviderId? Source { get; set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs index 1871e0f10..21d403979 100644 --- a/Jellyfin.Data/Entities/Libraries/Release.cs +++ b/Jellyfin.Data/Entities/Libraries/Release.cs @@ -45,7 +45,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs index 61714f909..da40a075f 100644 --- a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs @@ -24,6 +24,6 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs index e1acd2d45..730deccae 100644 --- a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs @@ -30,7 +30,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } /// /// Gets or sets the plot. @@ -40,7 +40,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Plot { get; set; } + public string? Plot { get; set; } /// /// Gets or sets the tagline. @@ -50,7 +50,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Tagline { get; set; } + public string? Tagline { get; set; } /// /// Gets or sets the country code. @@ -60,7 +60,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets a collection containing the networks. @@ -68,7 +68,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection Networks { get; protected set; } /// - [NotMapped] public ICollection Companies => Networks; } } diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 40f2f8ede..a8813ab88 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -46,7 +46,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 65535. /// - [Required] [MaxLength(65535)] [StringLength(65535)] public string Value { get; set; } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 28e12adde..9aa809164 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -51,6 +51,7 @@ namespace Jellyfin.Data.Entities PasswordResetProviderId = passwordResetProviderId; AccessSchedules = new HashSet(); + DisplayPreferences = new HashSet(); ItemDisplayPreferences = new HashSet(); // Groups = new HashSet(); Permissions = new HashSet(); @@ -92,7 +93,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string Username { get; set; } @@ -105,7 +105,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(65535)] [StringLength(65535)] - public string Password { get; set; } + public string? Password { get; set; } /// /// Gets or sets the user's easy password, or null if none is set. @@ -115,7 +115,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(65535)] [StringLength(65535)] - public string EasyPassword { get; set; } + public string? EasyPassword { get; set; } /// /// Gets or sets a value indicating whether the user must update their password. @@ -133,7 +133,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(255)] [StringLength(255)] - public string AudioLanguagePreference { get; set; } + public string? AudioLanguagePreference { get; set; } /// /// Gets or sets the authentication provider id. @@ -141,7 +141,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string AuthenticationProviderId { get; set; } @@ -152,7 +151,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string PasswordResetProviderId { get; set; } @@ -209,7 +207,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(255)] [StringLength(255)] - public string SubtitleLanguagePreference { get; set; } + public string? SubtitleLanguagePreference { get; set; } /// /// Gets or sets a value indicating whether missing episodes should be displayed. @@ -304,7 +302,7 @@ namespace Jellyfin.Data.Entities /// Gets or sets the user's profile image. Can be null. /// // [ForeignKey("UserId")] - public virtual ImageInfo ProfileImage { get; set; } + public virtual ImageInfo? ProfileImage { get; set; } /// /// Gets or sets the user's display preferences. @@ -312,8 +310,7 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] - public virtual DisplayPreferences DisplayPreferences { get; set; } + public virtual ICollection DisplayPreferences { get; set; } /// /// Gets or sets the level of sync play permissions this user has. diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index a8ac45645..a2b6f074e 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -5,6 +5,7 @@ false true true + enable true true true diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs index 0340248bb..aa6015caa 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs @@ -86,7 +86,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session return name; } - private static string? GetPlaybackNotificationType(string mediaType) + private static string GetPlaybackNotificationType(string mediaType) { if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { @@ -98,7 +98,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session return NotificationType.VideoPlayback.ToString(); } - return null; + return "Playback"; } } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 76d1389ca..b400a0dd1 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -184,8 +184,8 @@ namespace Jellyfin.Server.Implementations.Users var user = new User( name, - _defaultAuthenticationProvider.GetType().FullName, - _defaultPasswordResetProvider.GetType().FullName) + _defaultAuthenticationProvider.GetType().FullName!, + _defaultPasswordResetProvider.GetType().FullName!) { InternalId = max + 1 }; @@ -444,7 +444,7 @@ namespace Jellyfin.Server.Implementations.Users { var providerId = authenticationProvider.GetType().FullName; - if (!string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase)) + if (providerId != null && !string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase)) { user.AuthenticationProviderId = providerId; await UpdateUserAsync(user).ConfigureAwait(false); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 33f039c39..6d318d232 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -1,7 +1,5 @@ using System; -using System.Globalization; using System.IO; -using System.Linq; using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Serialization; using Jellyfin.Data.Entities; @@ -104,7 +102,7 @@ namespace Jellyfin.Server.Migrations.Routines _ => policy.LoginAttemptsBeforeLockout }; - var user = new User(mockup.Name, policy.AuthenticationProviderId, policy.PasswordResetProviderId) + var user = new User(mockup.Name, policy.AuthenticationProviderId!, policy.PasswordResetProviderId!) { Id = entry[1].ReadGuidFromBlob(), InternalId = entry[0].ToInt64(), diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 935a79031..142cebd0c 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Controller.Drawing string GetImageCacheTag(BaseItem item, ChapterInfo info); - string GetImageCacheTag(User user); + string? GetImageCacheTag(User user); /// /// Processes the image. diff --git a/tests/Jellyfin.Api.Tests/TestHelpers.cs b/tests/Jellyfin.Api.Tests/TestHelpers.cs index f27cdf7b6..c1549561d 100644 --- a/tests/Jellyfin.Api.Tests/TestHelpers.cs +++ b/tests/Jellyfin.Api.Tests/TestHelpers.cs @@ -26,8 +26,8 @@ namespace Jellyfin.Api.Tests { var user = new User( "jellyfin", - typeof(DefaultAuthenticationProvider).FullName, - typeof(DefaultPasswordResetProvider).FullName); + typeof(DefaultAuthenticationProvider).FullName!, + typeof(DefaultPasswordResetProvider).FullName!); // Set administrator flag. user.SetPermission(PermissionKind.IsAdministrator, role.Equals(UserRoles.Administrator, StringComparison.OrdinalIgnoreCase)); From 7b37ae94f7773069b1406728e297a012e3f726cc Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Mar 2021 20:02:42 -0500 Subject: [PATCH 058/402] Remove unused factory method --- Jellyfin.Data/Entities/AccessSchedule.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs index 9ac0666ac..7974d3add 100644 --- a/Jellyfin.Data/Entities/AccessSchedule.cs +++ b/Jellyfin.Data/Entities/AccessSchedule.cs @@ -58,18 +58,5 @@ namespace Jellyfin.Data.Entities /// /// The end hour. public double EndHour { get; set; } - - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The day of the week. - /// The start hour. - /// The end hour. - /// The associated user's id. - /// The newly created instance. - public static AccessSchedule Create(DynamicDayOfWeek dayOfWeek, double startHour, double endHour, Guid userId) - { - return new AccessSchedule(dayOfWeek, startHour, endHour, userId); - } } } From 7c413a323b0d22a59532687b854ea228d544ecb7 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Mar 2021 20:07:55 -0500 Subject: [PATCH 059/402] Move EF Core dependency out of Jellyfin.Data --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 1 + Jellyfin.Data/Jellyfin.Data.csproj | 3 +-- .../Jellyfin.Server.Implementations.csproj | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index f03f04e02..93a4c3a7d 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -27,6 +27,7 @@ + diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index a2b6f074e..8651dee25 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -42,8 +42,7 @@ - - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 4f24da0ee..e3278cfd0 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -26,6 +26,8 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive From 60ffa6f514656ed7256b555d483771c36164fce7 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 7 Mar 2021 14:43:28 +0100 Subject: [PATCH 060/402] Use FileShare.None when creating files --- Emby.Dlna/DlnaManager.cs | 3 ++- Emby.Server.Implementations/AppBase/ConfigurationHelper.cs | 3 ++- Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs | 6 ++++-- Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 6 ++++-- .../LiveTv/EmbyTV/EncodedRecorder.cs | 3 ++- .../LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 3 ++- .../LiveTv/TunerHosts/SharedHttpStream.cs | 3 ++- Jellyfin.Api/Controllers/ItemLookupController.cs | 3 ++- Jellyfin.Api/Controllers/RemoteImageController.cs | 3 ++- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 3 ++- MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs | 3 ++- MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 3 ++- MediaBrowser.Providers/Manager/ImageSaver.cs | 3 ++- MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs | 3 ++- MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs | 3 ++- MediaBrowser.Providers/Subtitles/SubtitleManager.cs | 3 ++- 16 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 9ab324038..d7b75f979 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -395,7 +395,8 @@ namespace Emby.Dlna { Directory.CreateDirectory(systemProfilesPath); - using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) { await stream.CopyToAsync(fileStream).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs index 77819c764..3f7076383 100644 --- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs +++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs @@ -53,7 +53,8 @@ namespace Emby.Server.Implementations.AppBase Directory.CreateDirectory(directory); // Save it after load in case we got new items - using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) { fs.Write(newBytes, 0, newBytesLen); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 341194f23..7a6b1d8b6 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -45,7 +45,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Directory.CreateDirectory(Path.GetDirectoryName(targetFile)); - using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None)) { onStarted(); @@ -70,7 +71,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Directory.CreateDirectory(Path.GetDirectoryName(targetFile)); - await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read); + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None); onStarted(); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 13b5a1c55..91a21db60 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1856,7 +1856,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.None)) { var settings = new XmlWriterSettings { @@ -1920,7 +1921,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.None)) { var settings = new XmlWriterSettings { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 78a82118e..40b934d32 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -91,7 +91,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true); await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false); await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index b16ccc561..08832bae1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -193,7 +193,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var resolved = false; - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)) { while (true) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index eeb2426f4..233c3d83a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -136,7 +136,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Logger.LogInformation("Beginning {0} stream to {1}", GetType().Name, TempFilePath); using var message = response; await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read); + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.None); await StreamHelper.CopyToAsync( stream, fileStream, diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index dfc68ffce..dabd4deb7 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -344,11 +344,12 @@ namespace Jellyfin.Api.Controllers Directory.CreateDirectory(directory); using (var stream = result.Content) { + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . await using var fileStream = new FileStream( fullCachePath, FileMode.Create, FileAccess.Write, - FileShare.Read, + FileShare.None, IODefaults.FileStreamBufferSize, true); diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index 5284888d8..e226adc64 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -259,7 +259,8 @@ namespace Jellyfin.Api.Controllers var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid."); Directory.CreateDirectory(fullCacheDirectory); - await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true); await response.Content.CopyToAsync(fileStream).ConfigureAwait(false); var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath)); diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 240d132b1..7cd9024b0 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -553,7 +553,8 @@ namespace Jellyfin.Api.Helpers $"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log"); // FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index a337521c6..e59fcb965 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -133,7 +133,8 @@ namespace MediaBrowser.LocalMetadata.Savers // On Windows, savint the file will fail if the file is hidden or readonly FileSystem.SetAttributes(path, false, false); - using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) { stream.CopyTo(filestream); } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index d19538730..fbb1563bb 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -677,7 +677,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (!string.Equals(text, newText, StringComparison.Ordinal)) { - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)) using (var writer = new StreamWriter(fileStream, encoding)) { await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 9dd87aef5..5bb85be7b 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -261,7 +261,8 @@ namespace MediaBrowser.Providers.Manager _fileSystem.SetAttributes(path, false, false); - await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) { await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index f463a3566..0a79f5bb5 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -171,7 +171,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true); await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index 7a15adb8e..4b1d91567 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -155,7 +155,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb Directory.CreateDirectory(Path.GetDirectoryName(path)); - await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true); await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 47e9d5ee8..d4d79d27b 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -228,7 +228,8 @@ namespace MediaBrowser.Providers.Subtitles { Directory.CreateDirectory(Path.GetDirectoryName(savePath)); - using (var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, FileStreamBufferSize, true)) + // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . + using (var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, true)) { await stream.CopyToAsync(fs).ConfigureAwait(false); } From 75c9659e05363f2e29e01757dc66f14f3f20f318 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 7 Mar 2021 14:17:32 +0000 Subject: [PATCH 061/402] Fix unreachable code & assign id to each profile. --- Emby.Dlna/Profiles/DefaultProfile.cs | 3 +++ Jellyfin.Api/Helpers/StreamingHelpers.cs | 17 +++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs index d4af72b62..8eaf12ba9 100644 --- a/Emby.Dlna/Profiles/DefaultProfile.cs +++ b/Emby.Dlna/Profiles/DefaultProfile.cs @@ -1,5 +1,7 @@ #pragma warning disable CS1591 +using System; +using System.Globalization; using System.Linq; using MediaBrowser.Model.Dlna; @@ -10,6 +12,7 @@ namespace Emby.Dlna.Profiles { public DefaultProfile() { + Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); Name = "Generic Device"; ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*"; diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index d20a02cf5..6bc5524cb 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -508,17 +508,18 @@ namespace Jellyfin.Api.Helpers private static void ApplyDeviceProfileSettings(StreamState state, IDlnaManager dlnaManager, IDeviceManager deviceManager, HttpRequest request, string? deviceProfileId, bool? @static) { - var headers = request.Headers; - if (!string.IsNullOrWhiteSpace(deviceProfileId)) { - state.DeviceProfile = dlnaManager.GetProfile(deviceProfileId); - } - else if (!string.IsNullOrWhiteSpace(deviceProfileId)) - { - var caps = deviceManager.GetCapabilities(deviceProfileId); + if (state.DeviceProfile == null) + { + state.DeviceProfile = dlnaManager.GetProfile(deviceProfileId); + } - state.DeviceProfile = caps == null ? dlnaManager.GetProfile(headers) : caps.DeviceProfile; + if (state.DeviceProfile == null) + { + var caps = deviceManager.GetCapabilities(deviceProfileId); + state.DeviceProfile = caps == null ? dlnaManager.GetProfile(request.Headers) : caps.DeviceProfile; + } } var profile = state.DeviceProfile; From e0db17a9354ff511c31c7bcf02c41d868ed91c5a Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 7 Mar 2021 22:49:31 +0100 Subject: [PATCH 062/402] do not throw ArgumentNullException in TryCleanString --- Emby.Naming/Video/CleanStringParser.cs | 11 +++++++++-- Emby.Naming/Video/VideoResolver.cs | 3 ++- tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs index 09a0cd189..deeea4dda 100644 --- a/Emby.Naming/Video/CleanStringParser.cs +++ b/Emby.Naming/Video/CleanStringParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace Emby.Naming.Video @@ -16,7 +17,7 @@ namespace Emby.Naming.Video /// List of regex to parse name and year from. /// Parsing result string. /// True if parsing was successful. - public static bool TryClean(string name, IReadOnlyList expressions, out ReadOnlySpan newName) + public static bool TryClean(string name, IReadOnlyList expressions, [NotNullWhen(true)] out ReadOnlySpan newName) { var len = expressions.Count; for (int i = 0; i < len; i++) @@ -31,8 +32,14 @@ namespace Emby.Naming.Video return false; } - private static bool TryClean(string name, Regex expression, out ReadOnlySpan newName) + private static bool TryClean(string name, Regex expression, [NotNullWhen(true)] out ReadOnlySpan newName) { + if (string.IsNullOrEmpty(name)) + { + newName = null; + return false; + } + var match = expression.Match(name); int index = match.Index; if (match.Success && index != 0) diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 619d1520e..d845d2ca6 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using Emby.Naming.Common; @@ -146,7 +147,7 @@ namespace Emby.Naming.Video /// Raw name. /// Clean name. /// True if cleaning of name was successful. - public bool TryCleanString(string name, out ReadOnlySpan newName) + public bool TryCleanString(string name, [NotNullWhen(true)] out ReadOnlySpan newName) { return CleanStringParser.TryClean(name, _options.CleanStringRegexes, out newName); } diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs index fde06c5a1..4b363843a 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs @@ -28,6 +28,7 @@ namespace Jellyfin.Naming.Tests.Video [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip.mkv", "Crouching.Tiger.Hidden.Dragon")] [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] [InlineData("Crouching.Tiger.Hidden.Dragon.4K.UltraHD.HDR.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData(null, null)] // FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")] public void CleanStringTest(string input, string expectedName) { From fcacae8cdeb3be15b27952d873ae08e29b6c7f94 Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 7 Mar 2021 22:59:08 +0100 Subject: [PATCH 063/402] return empty span instead of null for backwards compat --- Emby.Naming/Video/CleanStringParser.cs | 9 ++++----- Emby.Naming/Video/VideoResolver.cs | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs index deeea4dda..bd7553a91 100644 --- a/Emby.Naming/Video/CleanStringParser.cs +++ b/Emby.Naming/Video/CleanStringParser.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace Emby.Naming.Video @@ -17,7 +16,7 @@ namespace Emby.Naming.Video /// List of regex to parse name and year from. /// Parsing result string. /// True if parsing was successful. - public static bool TryClean(string name, IReadOnlyList expressions, [NotNullWhen(true)] out ReadOnlySpan newName) + public static bool TryClean(string name, IReadOnlyList expressions, out ReadOnlySpan newName) { var len = expressions.Count; for (int i = 0; i < len; i++) @@ -32,11 +31,11 @@ namespace Emby.Naming.Video return false; } - private static bool TryClean(string name, Regex expression, [NotNullWhen(true)] out ReadOnlySpan newName) + private static bool TryClean(string name, Regex expression, out ReadOnlySpan newName) { if (string.IsNullOrEmpty(name)) { - newName = null; + newName = ReadOnlySpan.Empty; return false; } @@ -48,7 +47,7 @@ namespace Emby.Naming.Video return true; } - newName = string.Empty; + newName = ReadOnlySpan.Empty; return false; } } diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index d845d2ca6..619d1520e 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using Emby.Naming.Common; @@ -147,7 +146,7 @@ namespace Emby.Naming.Video /// Raw name. /// Clean name. /// True if cleaning of name was successful. - public bool TryCleanString(string name, [NotNullWhen(true)] out ReadOnlySpan newName) + public bool TryCleanString(string name, out ReadOnlySpan newName) { return CleanStringParser.TryClean(name, _options.CleanStringRegexes, out newName); } From bc95268bd6a81db3f8e1c192aad48df5b879d231 Mon Sep 17 00:00:00 2001 From: WWWesten Date: Sun, 7 Mar 2021 21:40:56 +0000 Subject: [PATCH 064/402] Translated using Weblate (Kazakh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kk/ --- Emby.Server.Implementations/Localization/Core/kk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index a321e35d0..bdfb786c9 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -109,7 +109,7 @@ "TasksMaintenanceCategory": "Qyzmet körsetu", "Undefined": "Anyqtalmağan", "Forced": "Mäjbürlı", - "TaskDownloadMissingSubtitlesDescription": "Metaderekter teŋşelımderı negızınde joq subtitrlerdı Internetten ızdeidı.", + "TaskDownloadMissingSubtitlesDescription": "Metaderekter teŋşelımderı negızınde joq subtitrlerdı İnternetten ızdeidı.", "TaskRefreshChannelsDescription": "Internet-arnalar mälımetterın jaŋğyrtady.", "TaskCleanTranscodeDescription": "Bіr künnen asqan qaita kodtau faildaryn joiady.", "TaskUpdatePluginsDescription": "Avtomatty türde jaŋartuğa teŋşelgen plaginder üşın jaŋartulardy jüktep alady jäne ornatady.", From 2e62c09f2e55b33c2ba8eeb97157495e2f8ab5a9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 8 Mar 2021 02:16:35 +0100 Subject: [PATCH 065/402] Fix casing CollectionType --- .../Entities/JsonLowerCaseConverter.cs | 29 ++++++++ .../Entities/VirtualFolderInfo.cs | 2 + .../Entities/JsonLowerCaseConverterTests.cs | 70 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs create mode 100644 tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs diff --git a/MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs b/MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs new file mode 100644 index 000000000..7c627f0e3 --- /dev/null +++ b/MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs @@ -0,0 +1,29 @@ +#nullable disable +// THIS IS A HACK +// TODO: @bond Move to separate project + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Model.Entities +{ + /// + /// Converts an object to a lowercase string. + /// + /// The object type. + public class JsonLowerCaseConverter : JsonConverter + { + /// + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStringValue(value?.ToString().ToLowerInvariant()); + } + } +} diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs index ea3df3726..8fed392b9 100644 --- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs +++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs @@ -2,6 +2,7 @@ #pragma warning disable CS1591 using System; +using System.Text.Json.Serialization; using MediaBrowser.Model.Configuration; namespace MediaBrowser.Model.Entities @@ -35,6 +36,7 @@ namespace MediaBrowser.Model.Entities /// Gets or sets the type of the collection. /// /// The type of the collection. + [JsonConverter(typeof(JsonLowerCaseConverter))] public CollectionTypeOptions? CollectionType { get; set; } public LibraryOptions LibraryOptions { get; set; } diff --git a/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs b/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs new file mode 100644 index 000000000..955d296cc --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs @@ -0,0 +1,70 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using MediaBrowser.Model.Entities; +using Xunit; + +namespace Jellyfin.Model.Tests.Entities +{ + public class JsonLowerCaseConverterTests + { + private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() + { + Converters = + { + new JsonStringEnumConverter() + } + }; + + [Theory] + [InlineData(null, "{\"CollectionType\":null}")] + [InlineData(CollectionTypeOptions.Movies, "{\"CollectionType\":\"movies\"}")] + [InlineData(CollectionTypeOptions.MusicVideos, "{\"CollectionType\":\"musicvideos\"}")] + public void Serialize_CollectionTypeOptions_Correct(CollectionTypeOptions? collectionType, string expected) + { + Assert.Equal(expected, JsonSerializer.Serialize(new TestContainer(collectionType), _jsonOptions)); + } + + [Theory] + [InlineData("{\"CollectionType\":null}", null)] + [InlineData("{\"CollectionType\":\"movies\"}", CollectionTypeOptions.Movies)] + [InlineData("{\"CollectionType\":\"musicvideos\"}", CollectionTypeOptions.MusicVideos)] + public void Deserialize_CollectionTypeOptions_Correct(string json, CollectionTypeOptions? result) + { + var res = JsonSerializer.Deserialize(json, _jsonOptions); + Assert.NotNull(res); + Assert.Equal(result, res!.CollectionType); + } + + [Theory] + [InlineData(null)] + [InlineData(CollectionTypeOptions.Movies)] + [InlineData(CollectionTypeOptions.MusicVideos)] + public void RoundTrip_CollectionTypeOptions_Correct(CollectionTypeOptions? value) + { + var res = JsonSerializer.Deserialize(JsonSerializer.Serialize(new TestContainer(value), _jsonOptions), _jsonOptions); + Assert.NotNull(res); + Assert.Equal(value, res!.CollectionType); + } + + [Theory] + [InlineData("{\"CollectionType\":null}")] + [InlineData("{\"CollectionType\":\"movies\"}")] + [InlineData("{\"CollectionType\":\"musicvideos\"}")] + public void RoundTrip_String_Correct(string json) + { + var res = JsonSerializer.Serialize(JsonSerializer.Deserialize(json, _jsonOptions), _jsonOptions); + Assert.Equal(json, res); + } + + private class TestContainer + { + public TestContainer(CollectionTypeOptions? collectionType) + { + CollectionType = collectionType; + } + + [JsonConverter(typeof(JsonLowerCaseConverter))] + public CollectionTypeOptions? CollectionType { get; set; } + } + } +} From d4201f812cac5d1b5f4cdd618770df421b3fad70 Mon Sep 17 00:00:00 2001 From: Ikomhoog Date: Mon, 8 Mar 2021 11:02:51 +0100 Subject: [PATCH 066/402] Changed string.Length == 0 to string.IsNullOrEmpty in case of null --- Emby.Server.Implementations/Library/PathExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index d9e20e19a..7dcc925c2 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.Library { newPath = null; - if (path.Length == 0 || subPath.Length == 0 || newSubPath.Length == 0 || subPath.Length > path.Length) + if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(subPath) || string.IsNullOrEmpty(newSubPath) || subPath.Length > path.Length) { return false; } From 02122f28cc7dce1758f4a8e714dc8a8e7f4bb21f Mon Sep 17 00:00:00 2001 From: Ikomhoog Date: Mon, 8 Mar 2021 11:14:01 +0100 Subject: [PATCH 067/402] Update contributors --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1200275d5..954315f0e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -49,6 +49,7 @@ - [h1nk](https://github.com/h1nk) - [hawken93](https://github.com/hawken93) - [HelloWorld017](https://github.com/HelloWorld017) + - [ikomhoog](https://github.com/ikomhoog) - [jftuga](https://github.com/jftuga) - [joern-h](https://github.com/joern-h) - [joshuaboniface](https://github.com/joshuaboniface) From 54f81c4da484e3bb3d62669f0216c2a3a239166d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 8 Mar 2021 12:08:17 +0100 Subject: [PATCH 068/402] Call ToLower on CollectionTypeOptions.ToString --- Emby.Server.Implementations/Library/LibraryManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index d9ffe64b3..6d92e12b3 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1247,7 +1247,7 @@ namespace Emby.Server.Implementations.Library { // TODO: @bond use a ReadOnlySpan here when Enum.TryParse supports it // https://github.com/dotnet/runtime/issues/20008 - if (Enum.TryParse(Path.GetExtension(file), true, out var res)) + if (Enum.TryParse(Path.GetFileNameWithoutExtension(file), true, out var res)) { return res; } @@ -3001,7 +3001,7 @@ namespace Emby.Server.Implementations.Library if (collectionType != null) { - var path = Path.Combine(virtualFolderPath, collectionType.ToString() + ".collection"); + var path = Path.Combine(virtualFolderPath, collectionType.ToString().ToLowerInvariant() + ".collection"); File.WriteAllBytes(path, Array.Empty()); } From d3390302f9c3d62bb9c878f52cf11f9f00438cb1 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 8 Mar 2021 11:43:59 +0000 Subject: [PATCH 069/402] Update ApplicationHost.cs --- Emby.Server.Implementations/ApplicationHost.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 37047a4f7..49febdb96 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -43,6 +43,7 @@ using Emby.Server.Implementations.Serialization; using Emby.Server.Implementations.Session; using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; +using Emby.Server.Implementations.Udp; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; using Jellyfin.Networking.Configuration; @@ -233,7 +234,7 @@ namespace Emby.Server.Implementations /// /// Gets the value of the PublishedServerUrl setting. /// - public string PublishedServerUrl => _startupOptions.PublishedServerUrl ?? _startupConfig["PublishedServerUrl"]; + public string PublishedServerUrl => _startupOptions.PublishedServerUrl ?? _startupConfig[UdpServer.AddressOverrideConfigKey]; /// /// Gets the server configuration manager. From a031f7e410f7b599e1a50b2c54a7bd78f4187301 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 9 Mar 2021 00:07:21 +0000 Subject: [PATCH 070/402] Fix for multiple ip's in the same subnet per interface. --- Jellyfin.Networking/Manager/NetworkManager.cs | 37 ++++++++++--------- MediaBrowser.Common/Net/NetworkExtensions.cs | 5 ++- .../NetworkTesting/NetworkParseTests.cs | 2 + 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 51fcb6d9a..17bbf9633 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -157,15 +157,16 @@ namespace Jellyfin.Networking.Manager /// Creates a new network collection. /// /// Items to assign the collection, or null. + /// True if subnets that overlap should be merged (default). /// The collection created. - public static Collection CreateCollection(IEnumerable? source = null) + public static Collection CreateCollection(IEnumerable? source = null, bool unique = true) { var result = new Collection(); if (source != null) { foreach (var item in source) { - result.AddItem(item); + result.AddItem(item, unique); } } @@ -386,10 +387,12 @@ namespace Jellyfin.Networking.Manager } // Get the first LAN interface address that isn't a loopback. - var interfaces = CreateCollection(_interfaceAddresses - .Exclude(_bindExclusions) - .Where(IsInLocalNetwork) - .OrderBy(p => p.Tag)); + var interfaces = CreateCollection( + _interfaceAddresses + .Exclude(_bindExclusions) + .Where(IsInLocalNetwork) + .OrderBy(p => p.Tag), + false); if (interfaces.Count > 0) { @@ -429,11 +432,11 @@ namespace Jellyfin.Networking.Manager if (_bindExclusions.Count > 0) { // Return all the internal interfaces except the ones excluded. - return CreateCollection(_internalInterfaces.Where(p => !_bindExclusions.ContainsAddress(p))); + return CreateCollection(_internalInterfaces.Where(p => !_bindExclusions.ContainsAddress(p)), false); } // No bind address, so return all internal interfaces. - return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback())); + return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback()), false); } return new Collection(_bindAddresses); @@ -555,7 +558,7 @@ namespace Jellyfin.Networking.Manager && ((IsIP4Enabled && iface.Address.AddressFamily == AddressFamily.InterNetwork) || (IsIP6Enabled && iface.Address.AddressFamily == AddressFamily.InterNetworkV6))) { - result.AddItem(iface); + result.AddItem(iface, false); } } @@ -599,8 +602,8 @@ namespace Jellyfin.Networking.Manager var address = IPNetAddress.Parse(parts[0]); var index = int.Parse(parts[1], CultureInfo.InvariantCulture); address.Tag = index; - _interfaceAddresses.AddItem(address); - _interfaceNames.Add(parts[2], Math.Abs(index)); + _interfaceAddresses.AddItem(address, false); + _interfaceNames[parts[2]] = Math.Abs(index); } } @@ -977,7 +980,7 @@ namespace Jellyfin.Networking.Manager { _logger.LogDebug("Using LAN interface addresses as user provided no LAN details."); // Internal interfaces must be private and not excluded. - _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsPrivateAddressRange(i) && !_excludedSubnets.ContainsAddress(i))); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsPrivateAddressRange(i) && !_excludedSubnets.ContainsAddress(i)), false); // Subnets are the same as the calculated internal interface. _lanSubnets = new Collection(); @@ -1012,7 +1015,7 @@ namespace Jellyfin.Networking.Manager } // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet. - _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i))); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i)), false); } _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString()); @@ -1071,7 +1074,7 @@ namespace Jellyfin.Networking.Manager nw.Tag *= -1; } - _interfaceAddresses.AddItem(nw); + _interfaceAddresses.AddItem(nw, false); // Store interface name so we can use the name in Collections. _interfaceNames[adapter.Description.ToLower(CultureInfo.InvariantCulture)] = tag; @@ -1092,7 +1095,7 @@ namespace Jellyfin.Networking.Manager nw.Tag *= -1; } - _interfaceAddresses.AddItem(nw); + _interfaceAddresses.AddItem(nw, false); // Store interface name so we can use the name in Collections. _interfaceNames[adapter.Description.ToLower(CultureInfo.InvariantCulture)] = tag; @@ -1126,10 +1129,10 @@ namespace Jellyfin.Networking.Manager { _logger.LogWarning("No interfaces information available. Using loopback."); // Last ditch attempt - use loopback address. - _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback); + _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback, false); if (IsIP6Enabled) { - _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback); + _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback, false); } } } diff --git a/MediaBrowser.Common/Net/NetworkExtensions.cs b/MediaBrowser.Common/Net/NetworkExtensions.cs index 9c1a0cf49..cd0c2ea24 100644 --- a/MediaBrowser.Common/Net/NetworkExtensions.cs +++ b/MediaBrowser.Common/Net/NetworkExtensions.cs @@ -27,9 +27,10 @@ namespace MediaBrowser.Common.Net /// /// The . /// Item to add. - public static void AddItem(this Collection source, IPObject item) + /// True if subnets that overlap should be merged (default). + public static void AddItem(this Collection source, IPObject item, bool unique = true) { - if (!source.ContainsAddress(item)) + if (!source.ContainsAddress(item) || !unique) { source.Add(item); } diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs index 9f928ded1..a13822fcb 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs @@ -38,6 +38,8 @@ namespace Jellyfin.Networking.Tests [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")] // vEthernet1 and vEthernet212 should be excluded. [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")] + // Overlapping interface, + [InlineData("192.168.1.110/24,-20,br0|192.168.1.10/24,-16,br0|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.110/24,192.168.1.10/24]")] public void IgnoreVirtualInterfaces(string interfaces, string lan, string value) { var conf = new NetworkConfiguration() From 5241bd41ef4917e0a3071f961f08dd2eeec5a5dd Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 9 Mar 2021 01:28:21 +0100 Subject: [PATCH 071/402] Add code analysis attributes where appropriate --- Emby.Naming/Video/CleanStringParser.cs | 15 ++++---- Emby.Naming/Video/VideoResolver.cs | 3 +- .../Library/PathExtensions.cs | 11 ++++-- .../Video/CleanStringTests.cs | 36 ++++++++++--------- .../Library/PathExtensionsTests.cs | 6 +++- 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs index bd7553a91..4eef3ebc5 100644 --- a/Emby.Naming/Video/CleanStringParser.cs +++ b/Emby.Naming/Video/CleanStringParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace Emby.Naming.Video @@ -16,8 +17,14 @@ namespace Emby.Naming.Video /// List of regex to parse name and year from. /// Parsing result string. /// True if parsing was successful. - public static bool TryClean(string name, IReadOnlyList expressions, out ReadOnlySpan newName) + public static bool TryClean([NotNullWhen(true)] string? name, IReadOnlyList expressions, out ReadOnlySpan newName) { + if (string.IsNullOrEmpty(name)) + { + newName = ReadOnlySpan.Empty; + return false; + } + var len = expressions.Count; for (int i = 0; i < len; i++) { @@ -33,12 +40,6 @@ namespace Emby.Naming.Video private static bool TryClean(string name, Regex expression, out ReadOnlySpan newName) { - if (string.IsNullOrEmpty(name)) - { - newName = ReadOnlySpan.Empty; - return false; - } - var match = expression.Match(name); int index = match.Index; if (match.Success && index != 0) diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 619d1520e..79a6da8f7 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using Emby.Naming.Common; @@ -146,7 +147,7 @@ namespace Emby.Naming.Video /// Raw name. /// Clean name. /// True if cleaning of name was successful. - public bool TryCleanString(string name, out ReadOnlySpan newName) + public bool TryCleanString([NotNullWhen(true)] string? name, out ReadOnlySpan newName) { return CleanStringParser.TryClean(name, _options.CleanStringRegexes, out newName); } diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 7dcc925c2..57d0c26b9 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -59,11 +59,18 @@ namespace Emby.Server.Implementations.Library /// The result of the sub path replacement /// The path after replacing the sub path. /// , or is empty. - public static bool TryReplaceSubPath(this string path, string subPath, string newSubPath, [NotNullWhen(true)] out string? newPath) + public static bool TryReplaceSubPath( + [NotNullWhen(true)] this string? path, + [NotNullWhen(true)] string? subPath, + [NotNullWhen(true)] string? newSubPath, + [NotNullWhen(true)] out string? newPath) { newPath = null; - if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(subPath) || string.IsNullOrEmpty(newSubPath) || subPath.Length > path.Length) + if (string.IsNullOrEmpty(path) + || string.IsNullOrEmpty(subPath) + || string.IsNullOrEmpty(newSubPath) + || subPath.Length > path.Length) { return false; } diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs index 4b363843a..a720bdade 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs @@ -7,18 +7,13 @@ namespace Jellyfin.Naming.Tests.Video { public sealed class CleanStringTests { - private readonly NamingOptions _namingOptions = new NamingOptions(); + private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions()); [Theory] [InlineData("Super movie 480p.mp4", "Super movie")] [InlineData("Super movie 480p 2001.mp4", "Super movie")] [InlineData("Super movie [480p].mp4", "Super movie")] [InlineData("480 Super movie [tmdbid=12345].mp4", "480 Super movie")] - [InlineData("Super movie(2009).mp4", "Super movie(2009).mp4")] - [InlineData("Run lola run (lola rennt) (2009).mp4", "Run lola run (lola rennt) (2009).mp4")] - [InlineData(@"American.Psycho.mkv", "American.Psycho.mkv")] - [InlineData(@"American Psycho.mkv", "American Psycho.mkv")] - [InlineData(@"[rec].mkv", "[rec].mkv")] [InlineData("Crouching.Tiger.Hidden.Dragon.4k.mkv", "Crouching.Tiger.Hidden.Dragon")] [InlineData("Crouching.Tiger.Hidden.Dragon.UltraHD.mkv", "Crouching.Tiger.Hidden.Dragon")] [InlineData("Crouching.Tiger.Hidden.Dragon.UHD.mkv", "Crouching.Tiger.Hidden.Dragon")] @@ -28,19 +23,26 @@ namespace Jellyfin.Naming.Tests.Video [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip.mkv", "Crouching.Tiger.Hidden.Dragon")] [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] [InlineData("Crouching.Tiger.Hidden.Dragon.4K.UltraHD.HDR.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] - [InlineData(null, null)] // FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")] - public void CleanStringTest(string input, string expectedName) + public void CleanStringTest_NeedsCleaning_Success(string input, string expectedName) { - if (new VideoResolver(_namingOptions).TryCleanString(input, out ReadOnlySpan newName)) - { - // TODO: compare spans when XUnit supports it - Assert.Equal(expectedName, newName.ToString()); - } - else - { - Assert.Equal(expectedName, input); - } + Assert.True(_videoResolver.TryCleanString(input, out ReadOnlySpan newName)); + // TODO: compare spans when XUnit supports it + Assert.Equal(expectedName, newName.ToString()); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("Super movie(2009).mp4")] + [InlineData("[rec].mkv")] + [InlineData("American.Psycho.mkv")] + [InlineData("American Psycho.mkv")] + [InlineData("Run lola run (lola rennt) (2009).mp4")] + public void CleanStringTest_DoesntNeedCleaning_False(string? input) + { + Assert.False(_videoResolver.TryCleanString(input, out ReadOnlySpan newName)); + Assert.True(newName.IsEmpty); } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index a6fe90566..e5508243f 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -40,12 +40,16 @@ namespace Jellyfin.Server.Implementations.Tests.Library } [Theory] + [InlineData(null, null, null)] + [InlineData(null, "/my/path", "/another/path")] + [InlineData("/my/path", null, "/another/path")] + [InlineData("/my/path", "/another/path", null)] [InlineData("", "", "")] [InlineData("/my/path", "", "")] [InlineData("", "/another/path", "")] [InlineData("", "", "/new/subpath")] [InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/not jeff's band", "/home/not jeff")] - public void TryReplaceSubPath_InvalidInput_ReturnsFalseAndNull(string path, string subPath, string newSubPath) + public void TryReplaceSubPath_InvalidInput_ReturnsFalseAndNull(string? path, string? subPath, string? newSubPath) { Assert.False(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result)); Assert.Null(result); From 9ed7f429c01c3f54a154442250d3447fd66d1b02 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 9 Mar 2021 03:04:47 +0100 Subject: [PATCH 072/402] FxCop -> Net Analyzers (part 1) --- Emby.Dlna/Emby.Dlna.csproj | 1 - Emby.Drawing/Emby.Drawing.csproj | 1 - Emby.Naming/Emby.Naming.csproj | 1 - Emby.Notifications/Emby.Notifications.csproj | 1 - Emby.Photos/Emby.Photos.csproj | 1 - .../Emby.Server.Implementations.csproj | 1 - Jellyfin.Api/Jellyfin.Api.csproj | 1 - Jellyfin.Data/Jellyfin.Data.csproj | 1 - .../Jellyfin.Drawing.Skia.csproj | 1 - .../Jellyfin.Networking.csproj | 1 - .../Jellyfin.Server.Implementations.csproj | 1 - Jellyfin.Server/Jellyfin.Server.csproj | 1 - Jellyfin.sln | 14 +-- .../MediaBrowser.Common.csproj | 1 - .../MediaBrowser.Controller.csproj | 1 - .../MediaBrowser.LocalMetadata.csproj | 1 - .../BdInfo/BdInfoDirectoryInfo.cs | 2 +- .../Encoder/MediaEncoder.cs | 92 +++---------------- MediaBrowser.MediaEncoding/FfmpegException.cs | 39 ++++++++ .../MediaBrowser.MediaEncoding.csproj | 8 +- .../Probing/ProbeResultNormalizer.cs | 18 +++- .../Subtitles/SubtitleEncoder.cs | 6 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 9 +- .../MediaBrowser.Providers.csproj | 8 +- .../MediaBrowser.XbmcMetadata.csproj | 8 +- .../Parsers/BaseNfoParser.cs | 4 +- .../Providers/BaseVideoNfoProvider.cs | 2 +- .../Savers/AlbumNfoSaver.cs | 18 ++-- .../Savers/ArtistNfoSaver.cs | 13 ++- .../Savers/BaseNfoSaver.cs | 16 +--- .../Savers/EpisodeNfoSaver.cs | 29 +++--- .../Savers/MovieNfoSaver.cs | 17 ++-- .../Savers/SeasonNfoSaver.cs | 11 +-- .../Savers/SeriesNfoSaver.cs | 21 ++--- jellyfin.ruleset | 8 +- .../Jellyfin.Api.Tests.csproj | 8 +- .../Jellyfin.Common.Tests.csproj | 8 +- .../Jellyfin.Controller.Tests.csproj | 8 +- .../Jellyfin.Dlna.Tests.csproj | 8 +- .../Jellyfin.MediaEncoding.Tests.csproj | 8 +- .../Jellyfin.Model.Tests.csproj | 8 +- .../AudioBook/AudioBookResolverTests.cs | 4 +- .../Jellyfin.Naming.Tests.csproj | 10 +- .../Video/VideoResolverTests.cs | 4 +- .../Jellyfin.Networking.Tests.csproj | 17 ++-- .../{NetworkTesting => }/NetworkParseTests.cs | 29 +++--- ...llyfin.Server.Implementations.Tests.csproj | 8 +- .../Jellyfin.XbmcMetadata.Tests.csproj | 8 +- 48 files changed, 213 insertions(+), 273 deletions(-) create mode 100644 MediaBrowser.MediaEncoding/FfmpegException.cs rename tests/Jellyfin.Networking.Tests/{NetworkTesting => }/Jellyfin.Networking.Tests.csproj (73%) rename tests/Jellyfin.Networking.Tests/{NetworkTesting => }/NetworkParseTests.cs (98%) diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 8b057a095..480621dd7 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -25,7 +25,6 @@ - diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 7d479a5c6..5c5afe1c6 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -25,7 +25,6 @@ - diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index b43203e9d..63116f368 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -44,7 +44,6 @@ - diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 16ee918c4..526a27229 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -25,7 +25,6 @@ - diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 62e33e6c4..e64a658c5 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -28,7 +28,6 @@ - diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index f03f04e02..5a9792b51 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -49,7 +49,6 @@ - diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 67d0a3b5a..d5372d752 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -28,7 +28,6 @@ - diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index a8ac45645..42731bb11 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -34,7 +34,6 @@ - diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 466a12e67..1a8415ae0 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -32,7 +32,6 @@ - diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index cbda74361..f89a18426 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -13,7 +13,6 @@ - diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 4f24da0ee..19c7ac567 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -14,7 +14,6 @@ - diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index bf4f80669..6bfb5b878 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -26,7 +26,6 @@ - diff --git a/Jellyfin.sln b/Jellyfin.sln index d83013dab..02ac1c7e9 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -68,14 +68,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Implementat EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jellyfin.Networking\Jellyfin.Networking.csproj", "{0A3FCC4D-C714-4072-B90F-E374A15F9FF9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\NetworkTesting\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -190,10 +190,6 @@ Global {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Release|Any CPU.Build.0 = Release|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -206,6 +202,10 @@ Global {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}.Release|Any CPU.Build.0 = Release|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -217,10 +217,10 @@ Global {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {30922383-D513-4F4D-B890-A940B57FA353} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 8bb30c565..34e1934e2 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -46,7 +46,6 @@ - diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6b1c096ac..d487a324f 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -47,7 +47,6 @@ - diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 3ce9ff4cc..1792f1d9b 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -24,7 +24,6 @@ - diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs index 4a54b677d..ef9943722 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -71,7 +71,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo _impl.FullName, new[] { searchPattern }, false, - searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(), + (searchOption & System.IO.SearchOption.AllDirectories) == System.IO.SearchOption.AllDirectories).ToArray(), x => new BdInfoFileInfo(x)); } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a52019384..8a25a64c7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -448,7 +448,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (result == null || (result.Streams == null && result.Format == null)) { - throw new Exception("ffprobe failed - streams and format are both null."); + throw new FfmpegException("ffprobe failed - streams and format are both null."); } if (result.Streams != null) @@ -571,32 +571,18 @@ namespace MediaBrowser.MediaEncoding.Encoder // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar. // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar - var vf = string.Empty; - - if (threedFormat.HasValue) + var vf = threedFormat switch { - switch (threedFormat.Value) - { - case Video3DFormat.HalfSideBySide: - vf = "-vf crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. - break; - case Video3DFormat.FullSideBySide: - vf = "-vf crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // fsbs crop width in half,set the display aspect,crop out any black bars we may have made - break; - case Video3DFormat.HalfTopAndBottom: - vf = "-vf crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made - break; - case Video3DFormat.FullTopAndBottom: - vf = "-vf crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made - break; - default: - break; - } - } + // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. + Video3DFormat.HalfSideBySide => "-vf crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + // fsbs crop width in half,set the display aspect,crop out any black bars we may have made + Video3DFormat.FullSideBySide => "-vf crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made + Video3DFormat.HalfTopAndBottom => "-vf crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made + Video3DFormat.FullTopAndBottom => "-vf crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + _ => string.Empty + }; var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty; @@ -604,7 +590,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (enableHdrExtraction) { string tonemapFilters = "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p"; - if (string.IsNullOrEmpty(vf)) + if (vf.Length == 0) { vf = "-vf " + tonemapFilters; } @@ -633,35 +619,11 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads); - var probeSizeArgument = string.Empty; - var analyzeDurationArgument = string.Empty; - - if (!string.IsNullOrWhiteSpace(probeSizeArgument)) - { - args = probeSizeArgument + " " + args; - } - - if (!string.IsNullOrWhiteSpace(analyzeDurationArgument)) - { - args = analyzeDurationArgument + " " + args; - } - if (offset.HasValue) { args = string.Format(CultureInfo.InvariantCulture, "-ss {0} ", GetTimeParameter(offset.Value)) + args; } - if (videoStream != null) - { - /* fix - var decoder = encodinghelper.GetHardwareAcceleratedVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions()); - if (!string.IsNullOrWhiteSpace(decoder)) - { - args = decoder + " " + args; - } - */ - } - if (!string.IsNullOrWhiteSpace(container)) { var inputFormat = EncodingHelper.GetInputFormat(container); @@ -723,7 +685,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogError(msg); - throw new Exception(msg); + throw new FfmpegException(msg); } return tempExtractPath; @@ -770,30 +732,6 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads); - var probeSizeArgument = string.Empty; - var analyzeDurationArgument = string.Empty; - - if (!string.IsNullOrWhiteSpace(probeSizeArgument)) - { - args = probeSizeArgument + " " + args; - } - - if (!string.IsNullOrWhiteSpace(analyzeDurationArgument)) - { - args = analyzeDurationArgument + " " + args; - } - - if (videoStream != null) - { - /* fix - var decoder = encodinghelper.GetHardwareAcceleratedVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions()); - if (!string.IsNullOrWhiteSpace(decoder)) - { - args = decoder + " " + args; - } - */ - } - if (!string.IsNullOrWhiteSpace(container)) { var inputFormat = EncodingHelper.GetInputFormat(container); @@ -872,7 +810,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogError(msg); - throw new Exception(msg); + throw new FfmpegException(msg); } } } diff --git a/MediaBrowser.MediaEncoding/FfmpegException.cs b/MediaBrowser.MediaEncoding/FfmpegException.cs new file mode 100644 index 000000000..1697fd33a --- /dev/null +++ b/MediaBrowser.MediaEncoding/FfmpegException.cs @@ -0,0 +1,39 @@ +using System; + +namespace MediaBrowser.MediaEncoding +{ + /// + /// Represents errors that occur during interaction with FFmpeg. + /// + public class FfmpegException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public FfmpegException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public FfmpegException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a + /// reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if + /// no inner exception is specified. + /// + public FfmpegException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 3d6b4f98a..de00920ba 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -10,6 +10,9 @@ false true true + true + AllEnabledByDefault + ../jellyfin.ruleset @@ -30,13 +33,8 @@ - - ../jellyfin.ruleset - - - diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index b9cb49cf2..75067315f 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -640,7 +640,7 @@ namespace MediaBrowser.MediaEncoding.Probing } // Filter out junk - if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && streamInfo.CodecTagString.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1) + if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && !streamInfo.CodecTagString.Contains("[0]", StringComparison.OrdinalIgnoreCase)) { stream.CodecTag = streamInfo.CodecTagString; } @@ -1500,11 +1500,23 @@ namespace MediaBrowser.MediaEncoding.Probing } else { - throw new Exception(); // Switch to default parsing + // Switch to default parsing + if (subtitle.Contains('.', StringComparison.Ordinal)) + { + // skip the comment, keep the subtitle + description = string.Join('.', subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first + } + else + { + description = subtitle.Trim(); // Clean up whitespaces and save it + } } } - catch // Default parsing + catch (Exception ex) { + _logger.LogError(ex, "Error while parsing subtitle field"); + + // Default parsing if (subtitle.Contains('.', StringComparison.Ordinal)) { // skip the comment, keep the subtitle diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index fbb1563bb..39bec8da1 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -25,7 +25,7 @@ using UtfUnknown; namespace MediaBrowser.MediaEncoding.Subtitles { - public class SubtitleEncoder : ISubtitleEncoder + public sealed class SubtitleEncoder : ISubtitleEncoder { private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; @@ -484,7 +484,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { _logger.LogError("ffmpeg subtitle conversion failed for {Path}", inputPath); - throw new Exception( + throw new FfmpegException( string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle conversion failed for {0}", inputPath)); } @@ -637,7 +637,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _logger.LogError(msg); - throw new Exception(msg); + throw new FfmpegException(msg); } else { diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index b6d916913..30403219f 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -19,7 +19,9 @@ true true enable - latest + true + + ../jellyfin.ruleset true true true @@ -44,7 +46,6 @@ - @@ -53,8 +54,4 @@ - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 071a149db..5e7b8043f 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -30,20 +30,18 @@ false true true + true + AllEnabledByDefault + ../jellyfin.ruleset - - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 40f06c731..95327d3ae 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -20,18 +20,16 @@ true true enable + true + AllEnabledByDefault + ../jellyfin.ruleset - - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 6f164caf3..c4bbaf301 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -1168,11 +1168,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// IEnumerable{System.String}. private IEnumerable SplitNames(string value) { - value = value ?? string.Empty; - // Only split by comma if there is no pipe in the string // We have to be careful to not split names like Matthew, Jr. - var separator = value.IndexOf('|', StringComparison.Ordinal) == -1 && value.IndexOf(';', StringComparison.Ordinal) == -1 + var separator = !value.Contains('|', StringComparison.Ordinal) && !value.Contains(';', StringComparison.Ordinal) ? new[] { ',' } : new[] { '|', ';' }; diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs index af722748b..64cfc098f 100644 --- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; - public BaseVideoNfoProvider( + protected BaseVideoNfoProvider( ILogger> logger, IFileSystem fileSystem, IConfigurationManager config, diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs index c22f77dcd..2385e7048 100644 --- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs @@ -96,18 +96,16 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange( - new string[] - { - "track", - "artist", - "albumartist" - }); + foreach (var tag in base.GetTagsUsed(item)) + { + yield return tag; + } - return list; + yield return "track"; + yield return "artist"; + yield return "albumartist"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs index 6365cdecb..71b58cddb 100644 --- a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs @@ -88,16 +88,15 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "album", - "disbanded" - }); + yield return tag; + } - return list; + yield return "album"; + yield return "disbanded"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 0edab3787..3be35e2d9 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -166,19 +166,16 @@ namespace MediaBrowser.XbmcMetadata.Savers /// public abstract bool IsEnabledFor(BaseItem item, ItemUpdateType updateType); - protected virtual List GetTagsUsed(BaseItem item) + protected virtual IEnumerable GetTagsUsed(BaseItem item) { - var list = new List(); foreach (var providerKey in item.ProviderIds.Keys) { var providerIdTagName = GetTagForProviderKey(providerKey); if (!_commonTags.Contains(providerIdTagName)) { - list.Add(providerIdTagName); + yield return providerIdTagName; } } - - return list; } /// @@ -261,7 +258,7 @@ namespace MediaBrowser.XbmcMetadata.Savers AddMediaInfo(hasMediaSources, writer); } - var tagsUsed = GetTagsUsed(item); + var tagsUsed = GetTagsUsed(item).ToList(); try { @@ -351,10 +348,7 @@ namespace MediaBrowser.XbmcMetadata.Savers } var scanType = stream.IsInterlaced ? "interlaced" : "progressive"; - if (!string.IsNullOrEmpty(scanType)) - { - writer.WriteElementString("scantype", scanType); - } + writer.WriteElementString("scantype", scanType); if (stream.Channels.HasValue) { @@ -968,7 +962,7 @@ namespace MediaBrowser.XbmcMetadata.Savers => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); - private void AddCustomTags(string path, List xmlTagsUsed, XmlWriter writer, ILogger logger) + private void AddCustomTags(string path, IReadOnlyCollection xmlTagsUsed, XmlWriter writer, ILogger logger) { var settings = new XmlReaderSettings() { diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs index 5d3d17893..62f80e81b 100644 --- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs @@ -111,24 +111,23 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "aired", - "season", - "episode", - "episodenumberend", - "airsafter_season", - "airsbefore_episode", - "airsbefore_season", - "displayseason", - "displayepisode", - "showtitle" - }); + yield return tag; + } - return list; + yield return "aired"; + yield return "season"; + yield return "episode"; + yield return "episodenumberend"; + yield return "airsafter_season"; + yield return "airsbefore_episode"; + yield return "airsbefore_season"; + yield return "displayseason"; + yield return "displayepisode"; + yield return "showtitle"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs index 841121735..412e8031b 100644 --- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs @@ -123,18 +123,17 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "album", - "artist", - "set", - "id" - }); + yield return tag; + } - return list; + yield return "album"; + yield return "artist"; + yield return "set"; + yield return "id"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs index 925a230bd..b9d73ba82 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs @@ -72,15 +72,14 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "seasonnumber" - }); + yield return tag; + } - return list; + yield return "seasonnumber"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs index 42285db76..083f22e5d 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs @@ -90,20 +90,19 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "id", - "episodeguide", - "season", - "episode", - "status", - "displayorder" - }); + yield return tag; + } - return list; + yield return "id"; + yield return "episodeguide"; + yield return "season"; + yield return "episode"; + yield return "status"; + yield return "displayorder"; } } } diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 81337390c..b012d2b00 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -38,7 +38,7 @@ - + @@ -53,6 +53,8 @@ + + @@ -61,7 +63,11 @@ + + + + diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 873ff0ab4..0d8176bb2 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -27,7 +30,6 @@ - @@ -37,10 +39,6 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 278f34109..78e3061f7 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -21,7 +24,6 @@ - @@ -32,8 +34,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index b02a68a3d..df1eb8617 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -21,7 +24,6 @@ - @@ -31,8 +33,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index 850db1c75..d173d5c93 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -5,6 +5,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -16,7 +19,6 @@ - @@ -26,8 +28,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index e729dbb09..84306e0f7 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -27,7 +30,6 @@ - @@ -37,8 +39,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index b6d2c63bd..b458c06ff 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -5,6 +5,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -16,7 +19,6 @@ - @@ -26,8 +28,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs index b3257ace3..ad63adadc 100644 --- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs @@ -10,7 +10,7 @@ namespace Jellyfin.Naming.Tests.AudioBook { private readonly NamingOptions _namingOptions = new NamingOptions(); - public static IEnumerable GetResolveFileTestData() + public static IEnumerable Resolve_ValidFileNameTestData() { yield return new object[] { @@ -36,7 +36,7 @@ namespace Jellyfin.Naming.Tests.AudioBook } [Theory] - [MemberData(nameof(GetResolveFileTestData))] + [MemberData(nameof(Resolve_ValidFileNameTestData))] public void Resolve_ValidFileName_Success(AudioBookFileInfo expectedResult) { var result = new AudioBookResolver(_namingOptions).Resolve(expectedResult.Path); diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index 99185c975..0f8a0333a 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -8,8 +8,11 @@ net5.0 false - enable true + enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -25,14 +28,9 @@ - - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index ba5eaf1af..9bbbe2970 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Naming.Tests.Video { private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions()); - public static IEnumerable GetResolveFileTestData() + public static IEnumerable ResolveFile_ValidFileNameTestData() { yield return new object[] { @@ -156,7 +156,7 @@ namespace Jellyfin.Naming.Tests.Video } [Theory] - [MemberData(nameof(GetResolveFileTestData))] + [MemberData(nameof(ResolveFile_ValidFileNameTestData))] public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult) { var result = _videoResolver.ResolveFile(expectedResult.Path); diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj similarity index 73% rename from tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj rename to tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index fd77397ba..61eead0e9 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -8,8 +8,11 @@ net5.0 false - enable true + enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -22,18 +25,18 @@ - + + - - + + - - ../../jellyfin-tests.ruleset - + DEBUG + diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs similarity index 98% rename from tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs rename to tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 9f928ded1..c3469035e 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -1,13 +1,13 @@ using System; +using System.Collections.ObjectModel; using System.Net; using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; -using Moq; using Microsoft.Extensions.Logging.Abstractions; +using Moq; using Xunit; -using System.Collections.ObjectModel; namespace Jellyfin.Networking.Tests { @@ -124,7 +124,6 @@ namespace Jellyfin.Networking.Tests Assert.True(IPNetAddress.TryParse(address, out _)); } - /// /// All should be invalid address strings. /// @@ -141,7 +140,6 @@ namespace Jellyfin.Networking.Tests Assert.False(IPHost.TryParse(address, out _)); } - /// /// Test collection parsing. /// @@ -152,19 +150,22 @@ namespace Jellyfin.Networking.Tests /// Excluded IP4 addresses from the collection. /// Network addresses of the collection. [Theory] - [InlineData("127.0.0.1#", + [InlineData( + "127.0.0.1#", "[]", "[]", "[]", "[]", "[]")] - [InlineData("!127.0.0.1", + [InlineData( + "!127.0.0.1", "[]", "[]", "[127.0.0.1/32]", "[127.0.0.1/32]", "[]")] - [InlineData("", + [InlineData( + "", "[]", "[]", "[]", @@ -177,7 +178,8 @@ namespace Jellyfin.Networking.Tests "[10.10.10.10/32]", "[10.10.10.10/32]", "[192.158.0.0/16,127.0.0.1/32,::1/128,fd23:184f:2029:0:3139:7386:67d7:d517/128]")] - [InlineData("192.158.1.2/255.255.0.0,192.169.1.2/8", + [InlineData( + "192.158.1.2/255.255.0.0,192.169.1.2/8", "[192.158.1.2/16,192.169.1.2/8]", "[192.158.1.2/16,192.169.1.2/8]", "[]", @@ -194,12 +196,12 @@ namespace Jellyfin.Networking.Tests { EnableIPV6 = true, EnableIPV4 = true, - }; + }; using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); // Test included. - Collection nc = nm.CreateIPCollection(settings.Split(","), false); + Collection nc = nm.CreateIPCollection(settings.Split(","), false); Assert.Equal(nc.AsString(), result1); // Test excluded. @@ -208,7 +210,7 @@ namespace Jellyfin.Networking.Tests conf.EnableIPV6 = false; nm.UpdateSettings(conf); - + // Test IP4 included. nc = nm.CreateIPCollection(settings.Split(","), false); Assert.Equal(nc.AsString(), result2); @@ -252,7 +254,6 @@ namespace Jellyfin.Networking.Tests throw new ArgumentNullException(nameof(result)); } - var conf = new NetworkConfiguration() { EnableIPV6 = true, @@ -378,7 +379,6 @@ namespace Jellyfin.Networking.Tests Assert.True(ncResult.Compare(resultCollection)); } - [Theory] [InlineData("10.1.1.1/32", "10.1.1.1")] [InlineData("192.168.1.254/32", "192.168.1.254/255.255.255.255")] @@ -455,7 +455,7 @@ namespace Jellyfin.Networking.Tests // On my system eth16 is internal, eth11 external (Windows defines the indexes). // // This test is to replicate how subnet bound ServerPublisherUri work throughout the system. - + // User on internal network, we're bound internal and external - so result is internal override. [InlineData("192.168.1.1", "192.168.1.0/24", "eth16,eth11", false, "192.168.1.0/24=internal.jellyfin", "internal.jellyfin")] @@ -479,7 +479,6 @@ namespace Jellyfin.Networking.Tests // User is internal, no binding - so result is the 1st internal, which is then overridden. [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "eth16=http://helloworld.com", "http://helloworld.com")] - public void TestBindInterfaceOverrides(string source, string lan, string bindAddresses, bool ipv6enabled, string publishedServers, string result) { if (lan == null) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 1ad8171be..8debb08c5 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset Jellyfin.Server.Implementations.Tests @@ -31,7 +34,6 @@ - @@ -42,8 +44,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index d6aab3f85..2bb94c81f 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -5,6 +5,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -23,7 +26,6 @@ - @@ -34,8 +36,4 @@ - - ../jellyfin-tests.ruleset - - From d202df6e8a1f36a253e9780e7ec1521bc0c4b75e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 9 Mar 2021 03:22:51 +0100 Subject: [PATCH 073/402] Remove useless line --- MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj | 1 - MediaBrowser.Model/MediaBrowser.Model.csproj | 1 - MediaBrowser.Providers/MediaBrowser.Providers.csproj | 1 - MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj | 1 - tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 1 - tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 1 - tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj | 1 - tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 1 - .../Jellyfin.MediaEncoding.Tests.csproj | 1 - tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj | 1 - tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 1 - tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj | 1 - .../Jellyfin.Server.Implementations.Tests.csproj | 1 - .../Jellyfin.XbmcMetadata.Tests.csproj | 1 - 14 files changed, 14 deletions(-) diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index de00920ba..39fb0b47c 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -10,7 +10,6 @@ false true true - true AllEnabledByDefault ../jellyfin.ruleset diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 30403219f..f622a042a 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -19,7 +19,6 @@ true true enable - true ../jellyfin.ruleset true diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 5e7b8043f..152ea664a 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -30,7 +30,6 @@ false true true - true AllEnabledByDefault ../jellyfin.ruleset diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 95327d3ae..2904b40ec 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -20,7 +20,6 @@ true true enable - true AllEnabledByDefault ../jellyfin.ruleset diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 0d8176bb2..a336d2aee 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -10,7 +10,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 78e3061f7..017a67e9f 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -10,7 +10,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index df1eb8617..6dec25aa4 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -10,7 +10,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index d173d5c93..5d52f94c0 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -5,7 +5,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 84306e0f7..4cc1d37ee 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -10,7 +10,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index b458c06ff..0c7e262f5 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -5,7 +5,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index 0f8a0333a..cc12a99a6 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -10,7 +10,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index 61eead0e9..a76c0e9a0 100644 --- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -10,7 +10,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 8debb08c5..c3c258b68 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -10,7 +10,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset Jellyfin.Server.Implementations.Tests diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index 2bb94c81f..9380fe2af 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -5,7 +5,6 @@ false true enable - true AllEnabledByDefault ../jellyfin-tests.ruleset From e89bd8ba02cd5d5a98bba1bd8b1562af8fdaf5cf Mon Sep 17 00:00:00 2001 From: pkreuzt Date: Tue, 9 Mar 2021 01:58:03 +0000 Subject: [PATCH 074/402] Translated using Weblate (Galician) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gl/ --- .../Localization/Core/gl.json | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json index 12bcd793e..11139d32a 100644 --- a/Emby.Server.Implementations/Localization/Core/gl.json +++ b/Emby.Server.Implementations/Localization/Core/gl.json @@ -57,5 +57,27 @@ "DeviceOnlineWithName": "{0} conectouse", "DeviceOfflineWithName": "{0} desconectouse", "Default": "Por defecto", - "AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}" + "AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}", + "TaskCleanLogs": "Limpar Carpeta de Rexistros", + "TaskCleanActivityLog": "Limpar Rexistro de Actividade", + "TasksChannelsCategory": "Canáis de Internet", + "TaskUpdatePlugins": "Actualizar Plugins", + "User": "Usuario", + "Undefined": "Sen definir", + "TvShows": "Programas de TV", + "System": "Sistema", + "Sync": "Sincronizar", + "SubtitleDownloadFailureFromForItem": "Fallou a descarga de subtítulos para {1} dende {0}", + "StartupEmbyServerIsLoading": "O Servidor Jellyfin está cargando. Por favor, reinténteo en breve.", + "Songs": "Cancións", + "Shows": "Programas", + "ServerNameNeedsToBeRestarted": "{0} precisa ser reiniciado", + "ScheduledTaskStartedWithName": "{0} comezou", + "ScheduledTaskFailedWithName": "{0} fallou", + "ProviderValue": "Provedor: {0}", + "PluginUpdatedWithName": "{0} foi actualizado", + "PluginUninstalledWithName": "{0} foi desinstalado", + "PluginInstalledWithName": "{0} foi instalado", + "Playlists": "Listas de reproducción", + "Photos": "Fotos" } From 80fb52e64f9da4884eb9de5ff52865a3b7af6fc0 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 9 Mar 2021 10:46:54 +0100 Subject: [PATCH 075/402] Default to the searchinfo year, fallback to parsed year --- MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 2 +- MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 9a3e3d5fa..14be428ce 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -140,7 +140,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies // ParseName is required here. // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); - var searchResults = await _tmdbClientManager.SearchMovieAsync(parsedName.Name, parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var searchResults = await _tmdbClientManager.SearchMovieAsync(parsedName.Name, info.Year ?? parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 0238d0574..e18614524 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -207,7 +207,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // ParseName is required here. // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); - var searchResults = await _tmdbClientManager.SearchSeriesAsync(parsedName.Name, info.MetadataLanguage, info.Year ?? 0, cancellationToken).ConfigureAwait(false); + var searchResults = await _tmdbClientManager.SearchSeriesAsync(parsedName.Name, info.MetadataLanguage, info.Year ?? parsedName.Year ?? 0, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { From 880c8636bcfcbc14f60f8247105b3aa41de55c91 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 9 Mar 2021 11:44:39 +0100 Subject: [PATCH 076/402] Use imdbid as fallback in movie provider Includes post-ProviderIdExtensions cleanup --- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 9 ++ .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 82 ++++++++----------- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 9a3e3d5fa..8b5e2ddca 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -148,6 +148,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies } } + if (string.IsNullOrEmpty(tmdbId) && !string.IsNullOrEmpty(imdbId)) + { + var movieResultFromImdbId = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + if (movieResultFromImdbId?.MovieResults.Count > 0) + { + tmdbId = movieResultFromImdbId.MovieResults[0].Id.ToString(); + } + } + if (string.IsNullOrEmpty(tmdbId)) { return new MetadataResult(); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 0238d0574..5d3c94d71 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -43,9 +43,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public async Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); - - if (!string.IsNullOrEmpty(tmdbId)) + if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId)) { var series = await _tmdbClientManager .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, cancellationToken) @@ -59,9 +57,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } } - var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb); - - if (!string.IsNullOrEmpty(imdbId)) + if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out var imdbId)) { var findResult = await _tmdbClientManager .FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, searchInfo.MetadataLanguage, cancellationToken) @@ -82,9 +78,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } } - var tvdbId = searchInfo.GetProviderId(MetadataProvider.Tvdb); - - if (!string.IsNullOrEmpty(tvdbId)) + if (searchInfo.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId)) { var findResult = await _tmdbClientManager .FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, searchInfo.MetadataLanguage, cancellationToken) @@ -171,33 +165,21 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var tmdbId = info.GetProviderId(MetadataProvider.Tmdb); - if (string.IsNullOrEmpty(tmdbId)) + if (string.IsNullOrEmpty(tmdbId) && info.TryGetProviderId(MetadataProvider.Imdb, out var imdbId)) { - var imdbId = info.GetProviderId(MetadataProvider.Imdb); - - if (!string.IsNullOrEmpty(imdbId)) + var searchResult = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + if (searchResult?.TvResults.Count > 0) { - var searchResult = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - if (searchResult != null) - { - tmdbId = searchResult.TvResults.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture); - } + tmdbId = searchResult.TvResults[0].Id.ToString(CultureInfo.InvariantCulture); } } - if (string.IsNullOrEmpty(tmdbId)) + if (string.IsNullOrEmpty(tmdbId) && info.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId)) { - var tvdbId = info.GetProviderId(MetadataProvider.Tvdb); - - if (!string.IsNullOrEmpty(tvdbId)) + var searchResult = await _tmdbClientManager.FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + if (searchResult?.TvResults.Count > 0) { - var searchResult = await _tmdbClientManager.FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - if (searchResult != null) - { - tmdbId = searchResult.TvResults.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture); - } + tmdbId = searchResult.TvResults[0].Id.ToString(CultureInfo.InvariantCulture); } } @@ -215,32 +197,34 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } } - if (!string.IsNullOrEmpty(tmdbId)) + if (string.IsNullOrEmpty(tmdbId)) { - cancellationToken.ThrowIfCancellationRequested(); - - var tvShow = await _tmdbClientManager - .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) - .ConfigureAwait(false); - - result = new MetadataResult - { - Item = MapTvShowToSeries(tvShow, info.MetadataCountryCode), - ResultLanguage = info.MetadataLanguage ?? tvShow.OriginalLanguage - }; - - foreach (var person in GetPersons(tvShow)) - { - result.AddPerson(person); - } - - result.HasMetadata = result.Item != null; + return result; } + cancellationToken.ThrowIfCancellationRequested(); + + var tvShow = await _tmdbClientManager + .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .ConfigureAwait(false); + + result = new MetadataResult + { + Item = MapTvShowToSeries(tvShow, info.MetadataCountryCode), + ResultLanguage = info.MetadataLanguage ?? tvShow.OriginalLanguage + }; + + foreach (var person in GetPersons(tvShow)) + { + result.AddPerson(person); + } + + result.HasMetadata = result.Item != null; + return result; } - private Series MapTvShowToSeries(TvShow seriesResult, string preferredCountryCode) + private static Series MapTvShowToSeries(TvShow seriesResult, string preferredCountryCode) { var series = new Series { From fa8bfece4e72c32f8350aaa947c81b2494f6bb77 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Mar 2021 19:35:58 +0100 Subject: [PATCH 077/402] Split integration tests from unit tests --- Jellyfin.Server/Properties/AssemblyInfo.cs | 2 +- Jellyfin.sln | 2 + .../Jellyfin.Api.Tests.csproj | 7 +--- .../Controllers/BrandingControllerTests.cs | 2 +- .../Controllers/DashboardControllerTests.cs | 4 +- .../Jellyfin.Server.Integration.Tests.csproj | 40 +++++++++++++++++++ .../JellyfinApplicationFactory.cs | 2 +- .../OpenApiSpecTests.cs | 2 +- .../TestAppHost.cs | 2 +- .../TestPage.html | 0 .../TestPlugin.cs | 2 +- .../Jellyfin.Server.Tests.csproj | 39 ++++++++++++++++++ .../ParseNetworkTests.cs | 2 +- 13 files changed, 92 insertions(+), 14 deletions(-) rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/Controllers/BrandingControllerTests.cs (97%) rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/Controllers/DashboardControllerTests.cs (95%) create mode 100644 tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/JellyfinApplicationFactory.cs (99%) rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/OpenApiSpecTests.cs (97%) rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/TestAppHost.cs (98%) rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/TestPage.html (100%) rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/TestPlugin.cs (96%) create mode 100644 tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Tests}/ParseNetworkTests.cs (98%) diff --git a/Jellyfin.Server/Properties/AssemblyInfo.cs b/Jellyfin.Server/Properties/AssemblyInfo.cs index 7abf298b1..fe2d5c5f9 100644 --- a/Jellyfin.Server/Properties/AssemblyInfo.cs +++ b/Jellyfin.Server/Properties/AssemblyInfo.cs @@ -21,4 +21,4 @@ using System.Runtime.InteropServices; // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] -[assembly: InternalsVisibleTo("Jellyfin.Api.Tests")] +[assembly: InternalsVisibleTo("Jellyfin.Server.Tests")] diff --git a/Jellyfin.sln b/Jellyfin.sln index 02ac1c7e9..c749c51e6 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -76,6 +76,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index a336d2aee..577b61d02 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -35,11 +35,8 @@ - - - - - + + diff --git a/tests/Jellyfin.Api.Tests/Controllers/BrandingControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/BrandingControllerTests.cs similarity index 97% rename from tests/Jellyfin.Api.Tests/Controllers/BrandingControllerTests.cs rename to tests/Jellyfin.Server.Integration.Tests/Controllers/BrandingControllerTests.cs index 40933562d..fdd93f20e 100644 --- a/tests/Jellyfin.Api.Tests/Controllers/BrandingControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/BrandingControllerTests.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using MediaBrowser.Model.Branding; using Xunit; -namespace Jellyfin.Api.Tests +namespace Jellyfin.Server.Integration.Tests { public sealed class BrandingControllerTests : IClassFixture { diff --git a/tests/Jellyfin.Api.Tests/Controllers/DashboardControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs similarity index 95% rename from tests/Jellyfin.Api.Tests/Controllers/DashboardControllerTests.cs rename to tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs index 300b2697f..88905fc6d 100644 --- a/tests/Jellyfin.Api.Tests/Controllers/DashboardControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs @@ -8,7 +8,7 @@ using Jellyfin.Api.Models; using MediaBrowser.Common.Json; using Xunit; -namespace Jellyfin.Api.Tests.Controllers +namespace Jellyfin.Server.Integration.Tests.Controllers { public sealed class DashboardControllerTests : IClassFixture { @@ -39,7 +39,7 @@ namespace Jellyfin.Api.Tests.Controllers Assert.True(response.IsSuccessStatusCode); Assert.Equal(MediaTypeNames.Text.Html, response.Content.Headers.ContentType?.MediaType); - StreamReader reader = new StreamReader(typeof(TestPlugin).Assembly.GetManifestResourceStream("Jellyfin.Api.Tests.TestPage.html")!); + StreamReader reader = new StreamReader(typeof(TestPlugin).Assembly.GetManifestResourceStream("Jellyfin.Server.Integration.Tests.TestPage.html")!); Assert.Equal(await response.Content.ReadAsStringAsync(), reader.ReadToEnd()); } diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj new file mode 100644 index 000000000..49004966b --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -0,0 +1,40 @@ + + + net5.0 + false + true + enable + AllEnabledByDefault + ../jellyfin-tests.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs similarity index 99% rename from tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs rename to tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs index 92c5495c8..94e618102 100644 --- a/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; using Serilog; using Serilog.Extensions.Logging; -namespace Jellyfin.Api.Tests +namespace Jellyfin.Server.Integration.Tests { /// /// Factory for bootstrapping the Jellyfin application in memory for functional end to end tests. diff --git a/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs similarity index 97% rename from tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs rename to tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs index 03ab56d1f..3cbd638f9 100644 --- a/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs @@ -6,7 +6,7 @@ using MediaBrowser.Model.Branding; using Xunit; using Xunit.Abstractions; -namespace Jellyfin.Api.Tests +namespace Jellyfin.Server.Integration.Tests { public sealed class OpenApiSpecTests : IClassFixture { diff --git a/tests/Jellyfin.Api.Tests/TestAppHost.cs b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs similarity index 98% rename from tests/Jellyfin.Api.Tests/TestAppHost.cs rename to tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs index eb4c9b305..4e5d0fcb6 100644 --- a/tests/Jellyfin.Api.Tests/TestAppHost.cs +++ b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Tests +namespace Jellyfin.Server.Integration.Tests { /// /// Implementation of the abstract class. diff --git a/tests/Jellyfin.Api.Tests/TestPage.html b/tests/Jellyfin.Server.Integration.Tests/TestPage.html similarity index 100% rename from tests/Jellyfin.Api.Tests/TestPage.html rename to tests/Jellyfin.Server.Integration.Tests/TestPage.html diff --git a/tests/Jellyfin.Api.Tests/TestPlugin.cs b/tests/Jellyfin.Server.Integration.Tests/TestPlugin.cs similarity index 96% rename from tests/Jellyfin.Api.Tests/TestPlugin.cs rename to tests/Jellyfin.Server.Integration.Tests/TestPlugin.cs index a3b4b6994..1d67ac487 100644 --- a/tests/Jellyfin.Api.Tests/TestPlugin.cs +++ b/tests/Jellyfin.Server.Integration.Tests/TestPlugin.cs @@ -7,7 +7,7 @@ using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; -namespace Jellyfin.Api.Tests +namespace Jellyfin.Server.Integration.Tests { public class TestPlugin : BasePlugin, IHasWebPages { diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj new file mode 100644 index 000000000..65ea28e94 --- /dev/null +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -0,0 +1,39 @@ + + + + net5.0 + false + true + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../jellyfin-tests.ruleset + + + diff --git a/tests/Jellyfin.Api.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs similarity index 98% rename from tests/Jellyfin.Api.Tests/ParseNetworkTests.cs rename to tests/Jellyfin.Server.Tests/ParseNetworkTests.cs index 3984407ee..0b714e80a 100644 --- a/tests/Jellyfin.Api.Tests/ParseNetworkTests.cs +++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; -namespace Jellyfin.Api.Tests +namespace Jellyfin.Server.Tests { public class ParseNetworkTests { From afc70b28d4e1b13e75d12a40eab9455460be1c47 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Mar 2021 19:42:22 +0100 Subject: [PATCH 078/402] Add Jellyfin.Server.Tests to solution --- Jellyfin.sln | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Jellyfin.sln b/Jellyfin.sln index c749c51e6..415d02490 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -78,6 +78,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -208,6 +210,10 @@ Global {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU + {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -223,6 +229,7 @@ Global {30922383-D513-4F4D-B890-A940B57FA353} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} From bbbb811e72fef85f0ae95f195da42a924ef1f642 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Mar 2021 20:03:15 +0100 Subject: [PATCH 079/402] Fix azure --- .ci/azure-pipelines-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml index 95e0d8c58..7838b3b02 100644 --- a/.ci/azure-pipelines-test.yml +++ b/.ci/azure-pipelines-test.yml @@ -94,5 +94,5 @@ jobs: displayName: 'Publish OpenAPI Artifact' condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) inputs: - targetPath: "tests/Jellyfin.Api.Tests/bin/Release/net5.0/openapi.json" + targetPath: "tests/Jellyfin.Server.Integration.Tests/bin/Release/net5.0/openapi.json" artifactName: 'OpenAPI Spec' From 3471ddfc84b409a876ebdc71582195de8ad6934f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 2 Mar 2021 02:36:50 +0100 Subject: [PATCH 080/402] Fix duplicate project id in sln --- Jellyfin.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.sln b/Jellyfin.sln index 415d02490..7b81f4346 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -76,7 +76,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" EndProject From b9577d0fd986fcadd656a9438eba81f49f375ad8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 2 Mar 2021 02:57:36 +0100 Subject: [PATCH 081/402] Check for specific status code instead of success --- .../Controllers/BrandingControllerTests.cs | 3 ++- .../Controllers/DashboardControllerTests.cs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/BrandingControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/BrandingControllerTests.cs index fdd93f20e..87136dfc8 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/BrandingControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/BrandingControllerTests.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Net.Mime; using System.Text; using System.Text.Json; @@ -26,7 +27,7 @@ namespace Jellyfin.Server.Integration.Tests var response = await client.GetAsync("/Branding/Configuration"); // Assert - Assert.True(response.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType); Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet); var responseBody = await response.Content.ReadAsStreamAsync(); diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs index 88905fc6d..86d6326d8 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers var response = await client.GetAsync("/web/ConfigurationPage?name=TestPlugin").ConfigureAwait(false); - Assert.True(response.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(MediaTypeNames.Text.Html, response.Content.Headers.ContentType?.MediaType); StreamReader reader = new StreamReader(typeof(TestPlugin).Assembly.GetManifestResourceStream("Jellyfin.Server.Integration.Tests.TestPage.html")!); Assert.Equal(await response.Content.ReadAsStringAsync(), reader.ReadToEnd()); @@ -60,7 +60,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers var response = await client.GetAsync("/web/ConfigurationPages").ConfigureAwait(false); - Assert.True(response.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); var res = await response.Content.ReadAsStreamAsync(); _ = await JsonSerializer.DeserializeAsync(res, _jsonOpions); @@ -74,7 +74,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers var response = await client.GetAsync("/web/ConfigurationPages?enableInMainMenu=true").ConfigureAwait(false); - Assert.True(response.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType); Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet); From 37eb7d6d49b35067c6711ba6f74a64242da419b0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 5 Mar 2021 12:34:22 +0100 Subject: [PATCH 082/402] Perform static initialization only once --- .../JellyfinApplicationFactory.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs index 94e618102..d9ec81a27 100644 --- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs +++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs @@ -4,7 +4,6 @@ using System.IO; using System.Threading; using Emby.Server.Implementations; using Emby.Server.Implementations.IO; -using Jellyfin.Server; using MediaBrowser.Common; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; @@ -26,9 +25,9 @@ namespace Jellyfin.Server.Integration.Tests private readonly ConcurrentBag _disposableComponents = new ConcurrentBag(); /// - /// Initializes a new instance of the class. + /// Initializes static members of the class. /// - public JellyfinApplicationFactory() + static JellyfinApplicationFactory() { // Perform static initialization that only needs to happen once per test-run Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger(); From a618d6053f78664ecb27cb01216ddb21833f5f89 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 9 Mar 2021 14:09:28 +0100 Subject: [PATCH 083/402] Move TestPluginWithoutPages to the correct project --- .../TestPluginWithoutPages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{Jellyfin.Api.Tests => Jellyfin.Server.Integration.Tests}/TestPluginWithoutPages.cs (94%) diff --git a/tests/Jellyfin.Api.Tests/TestPluginWithoutPages.cs b/tests/Jellyfin.Server.Integration.Tests/TestPluginWithoutPages.cs similarity index 94% rename from tests/Jellyfin.Api.Tests/TestPluginWithoutPages.cs rename to tests/Jellyfin.Server.Integration.Tests/TestPluginWithoutPages.cs index 2d2f78a98..ac10c4784 100644 --- a/tests/Jellyfin.Api.Tests/TestPluginWithoutPages.cs +++ b/tests/Jellyfin.Server.Integration.Tests/TestPluginWithoutPages.cs @@ -6,7 +6,7 @@ using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; -namespace Jellyfin.Api.Tests +namespace Jellyfin.Server.Integration.Tests { public class TestPluginWithoutPages : BasePlugin { From eca3b37d6eae727bd46b7a719cbf5b70a84627f8 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 9 Mar 2021 17:01:05 +0100 Subject: [PATCH 084/402] Use FileShare.Read to fix HdHomeRun --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 3 +-- .../LiveTv/TunerHosts/SharedHttpStream.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 08832bae1..b16ccc561 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -193,8 +193,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var resolved = false; - // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)) + using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) { while (true) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 233c3d83a..eeb2426f4 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -136,8 +136,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Logger.LogInformation("Beginning {0} stream to {1}", GetType().Name, TempFilePath); using var message = response; await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . - await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.None); + await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read); await StreamHelper.CopyToAsync( stream, fileStream, From ece0d67f99240324aafdcde85def3e933e413b69 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 9 Mar 2021 17:31:31 +0100 Subject: [PATCH 085/402] Use FileShare.Read for log files --- Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs | 3 +-- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 40b934d32..78a82118e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -91,8 +91,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . - _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true); + _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false); await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false); diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 7cd9024b0..240d132b1 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -553,8 +553,7 @@ namespace Jellyfin.Api.Helpers $"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log"); // FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . - Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true); + Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); From 235b36a4c7f5e2dbb682bb7e6588b8f06219ce6c Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 9 Mar 2021 12:41:51 -0500 Subject: [PATCH 086/402] Remove Microsoft repo from install step This was breaking Fedora builds due to a mismatch. We can use the .NET SDK 5.0 from the Fedora 33 repos instead and this seems to work. --- deployment/Dockerfile.fedora.amd64 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 2549f25ee..137e56ecf 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -13,9 +13,7 @@ RUN dnf update -y \ && dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd # Install DotNET SDK -RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \ - && curl -o /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/$(rpm -E %fedora)/prod.repo \ - && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} +RUN dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} # Create symlinks and directories RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.amd64 /build.sh \ From c5a870051a097e346360b66d063d2f3492f25b0b Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 10 Mar 2021 08:20:02 +0100 Subject: [PATCH 087/402] Use distinct for artists to avoid double refreshing --- .../Entities/Audio/IHasAlbumArtist.cs | 12 +++--------- MediaBrowser.Controller/Library/NameExtensions.cs | 8 ++++---- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs index f6d3cd6cc..20fad4cb0 100644 --- a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs @@ -1,6 +1,8 @@ #pragma warning disable CS1591 using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.Entities.Audio { @@ -23,15 +25,7 @@ namespace MediaBrowser.Controller.Entities.Audio public static IEnumerable GetAllArtists(this T item) where T : IHasArtist, IHasAlbumArtist { - foreach (var i in item.AlbumArtists) - { - yield return i; - } - - foreach (var i in item.Artists) - { - yield return i; - } + return item.AlbumArtists.Concat(item.Artists).DistinctNames(); } } } diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs index 1c90bb4e0..6e79dc8dd 100644 --- a/MediaBrowser.Controller/Library/NameExtensions.cs +++ b/MediaBrowser.Controller/Library/NameExtensions.cs @@ -10,6 +10,10 @@ namespace MediaBrowser.Controller.Library { public static class NameExtensions { + public static IEnumerable DistinctNames(this IEnumerable names) + => names.GroupBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase) + .Select(x => x.First()); + private static string RemoveDiacritics(string? name) { if (name == null) @@ -19,9 +23,5 @@ namespace MediaBrowser.Controller.Library return name.RemoveDiacritics(); } - - public static IEnumerable DistinctNames(this IEnumerable names) - => names.GroupBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()); } } From 84da57cd4811d3bda6b3341c0a8a3b45b3aa72de Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 09:07:11 +0000 Subject: [PATCH 088/402] Update StreamingHelpers.cs Updated condition --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 6bc5524cb..e2306aa27 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -510,10 +510,7 @@ namespace Jellyfin.Api.Helpers { if (!string.IsNullOrWhiteSpace(deviceProfileId)) { - if (state.DeviceProfile == null) - { - state.DeviceProfile = dlnaManager.GetProfile(deviceProfileId); - } + state.DeviceProfile = dlnaManager.GetProfile(deviceProfileId); if (state.DeviceProfile == null) { From 3824c09e774f35df18f37b56e2fc6101dc2022fe Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 10 Mar 2021 10:47:35 +0100 Subject: [PATCH 089/402] fix multiversion eligibility check for complex folder names --- Emby.Naming/Video/VideoListResolver.cs | 19 ++++++++++-------- .../Video/MultiVersionTests.cs | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 09a030d2d..0b44763a2 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -221,20 +221,23 @@ namespace Emby.Naming.Video string testFilename = Path.GetFileNameWithoutExtension(testFilePath); if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase)) { - if (CleanStringParser.TryClean(testFilename, _options.CleanStringRegexes, out var cleanName)) - { - testFilename = cleanName.ToString(); - } - + // Remove the folder name before cleaning as we don't care about cleaning that part if (folderName.Length <= testFilename.Length) { testFilename = testFilename.Substring(folderName.Length).Trim(); } + if (CleanStringParser.TryClean(testFilename, _options.CleanStringRegexes, out var cleanName)) + { + testFilename = cleanName.ToString(); + } + + // The CleanStringParser should have removed common keywords etc., so if it starts with -, _ or [ it's eligible. return string.IsNullOrEmpty(testFilename) - || testFilename[0] == '-' - || testFilename[0] == '_' - || string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)); + || testFilename[0] == '-' + || testFilename[0] == '_' + || testFilename[0] == '[' + || string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)); } return false; diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index bc5e6fa63..a46caeca0 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -368,6 +368,26 @@ namespace Jellyfin.Naming.Tests.Video Assert.Single(result[0].AlternateVersions); } + [Fact] + public void Resolve_GivenFolderNameWithBracketsAndHyphens_GroupsBasedOnFolderName() + { + var files = new[] + { + @"/movies/John Wick - Kapitel 3 (2019) [imdbid=tt6146586]/John Wick - Kapitel 3 (2019) [imdbid=tt6146586] - Version 1.mkv", + @"/movies/John Wick - Kapitel 3 (2019) [imdbid=tt6146586]/John Wick - Kapitel 3 (2019) [imdbid=tt6146586] - Version 2.mkv" + }; + + var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Empty(result[0].Extras); + Assert.Single(result[0].AlternateVersions); + } + [Fact] public void TestEmptyList() { From 2fe26ef136fb00a59362bec33f0a4c31335a330a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 13:28:18 +0000 Subject: [PATCH 090/402] removed parameter preset --- Jellyfin.Networking/Manager/NetworkManager.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 17bbf9633..bf121737b 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -157,16 +157,15 @@ namespace Jellyfin.Networking.Manager /// Creates a new network collection. /// /// Items to assign the collection, or null. - /// True if subnets that overlap should be merged (default). /// The collection created. - public static Collection CreateCollection(IEnumerable? source = null, bool unique = true) + public static Collection CreateCollection(IEnumerable? source = null) { var result = new Collection(); if (source != null) { foreach (var item in source) { - result.AddItem(item, unique); + result.AddItem(item, false); } } @@ -391,8 +390,7 @@ namespace Jellyfin.Networking.Manager _interfaceAddresses .Exclude(_bindExclusions) .Where(IsInLocalNetwork) - .OrderBy(p => p.Tag), - false); + .OrderBy(p => p.Tag)); if (interfaces.Count > 0) { @@ -432,11 +430,11 @@ namespace Jellyfin.Networking.Manager if (_bindExclusions.Count > 0) { // Return all the internal interfaces except the ones excluded. - return CreateCollection(_internalInterfaces.Where(p => !_bindExclusions.ContainsAddress(p)), false); + return CreateCollection(_internalInterfaces.Where(p => !_bindExclusions.ContainsAddress(p))); } // No bind address, so return all internal interfaces. - return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback()), false); + return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback())); } return new Collection(_bindAddresses); @@ -980,7 +978,7 @@ namespace Jellyfin.Networking.Manager { _logger.LogDebug("Using LAN interface addresses as user provided no LAN details."); // Internal interfaces must be private and not excluded. - _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsPrivateAddressRange(i) && !_excludedSubnets.ContainsAddress(i)), false); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsPrivateAddressRange(i) && !_excludedSubnets.ContainsAddress(i))); // Subnets are the same as the calculated internal interface. _lanSubnets = new Collection(); @@ -1015,7 +1013,7 @@ namespace Jellyfin.Networking.Manager } // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet. - _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i)), false); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i))); } _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString()); From 8a74d7659819be55fd60dc8d272b0cca2f5178e2 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 10 Mar 2021 17:03:19 +0100 Subject: [PATCH 091/402] Fix id tag setting IMDb id when it is TMDb id --- MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs index 2d0eb8433..d30190a7e 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs @@ -47,12 +47,19 @@ namespace MediaBrowser.XbmcMetadata.Parsers { case "id": { + // get ids from attributes string? imdbId = reader.GetAttribute("IMDB"); string? tmdbId = reader.GetAttribute("TMDB"); - if (string.IsNullOrWhiteSpace(imdbId)) + // read id from content + var contentId = reader.ReadElementContentAsString(); + if (contentId.Contains("tt", StringComparison.Ordinal) && string.IsNullOrEmpty(imdbId)) { - imdbId = reader.ReadElementContentAsString(); + imdbId = contentId; + } + else if (string.IsNullOrEmpty(tmdbId)) + { + tmdbId = contentId; } if (!string.IsNullOrWhiteSpace(imdbId)) From 954148eb6de1f276fb584aa70b384babea0e58ce Mon Sep 17 00:00:00 2001 From: David Date: Wed, 10 Mar 2021 17:04:15 +0100 Subject: [PATCH 092/402] Fix Radarr url nfo files --- .../Parsers/BaseNfoParser.cs | 56 ++++++------------- .../Parsers/SeriesNfoParser.cs | 2 +- .../Parsers/MovieNfoParserTests.cs | 15 +++++ .../Test Data/Justice League.nfo | 3 +- .../Test Data/Radarr.nfo | 2 + 5 files changed, 38 insertions(+), 40 deletions(-) create mode 100644 tests/Jellyfin.XbmcMetadata.Tests/Test Data/Radarr.nfo diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index c4bbaf301..3129c131d 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -63,7 +63,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected virtual bool SupportsUrlAfterClosingXmlTag => false; - protected virtual string MovieDbParserSearchString => "themoviedb.org/movie/"; + protected virtual string TmdbRegex => "themoviedb\\.org\\/movie\\/([0-9]+)"; /// /// Fetches metadata for an item from one xml file. @@ -181,8 +181,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers } else { - // If the file is just an Imdb url, handle that - + // If the file is just provider urls, handle that ParseProviderLinks(item.Item, xml); return; @@ -221,50 +220,31 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected void ParseProviderLinks(T item, string xml) { - // Look for a match for the Regex pattern "tt" followed by 7 or 8 digits - var m = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.IgnoreCase); - if (m.Success) + // IMDB: + // https://www.imdb.com/title/tt4154796 + var imdbRegex = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.Compiled); + if (imdbRegex.Success) { - item.SetProviderId(MetadataProvider.Imdb, m.Value); + item.SetProviderId(MetadataProvider.Imdb, imdbRegex.Value); } - // Support Tmdb - // https://www.themoviedb.org/movie/30287-fallo - var srch = MovieDbParserSearchString; - var index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - - if (index != -1) + // TMDB: + // https://www.themoviedb.org/movie/30287-fallo (movie) + // https://www.themoviedb.org/tv/1668-friends (tv) + var tmdbRegex = Regex.Match(xml, TmdbRegex, RegexOptions.Compiled); + if (tmdbRegex.Success) { - var tmdbId = xml.AsSpan().Slice(index + srch.Length).TrimEnd('/'); - index = tmdbId.IndexOf('-'); - if (index != -1) - { - tmdbId = tmdbId.Slice(0, index); - } - - if (!tmdbId.IsEmpty - && !tmdbId.IsWhiteSpace() - && int.TryParse(tmdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - item.SetProviderId(MetadataProvider.Tmdb, value.ToString(UsCulture)); - } + item.SetProviderId(MetadataProvider.Tmdb, tmdbRegex.Groups[1].Value); } + // TVDB: + // https://www.thetvdb.com/?tab=series&id=121361 if (item is Series) { - srch = "thetvdb.com/?tab=series&id="; - - index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - - if (index != -1) + var tvdbRegex = Regex.Match(xml, "thetvdb\\.com\\/\\?tab=series\\&id=([0-9]+)", RegexOptions.Compiled); + if (tvdbRegex.Success) { - var tvdbId = xml.AsSpan().Slice(index + srch.Length).TrimEnd('/'); - if (!tvdbId.IsEmpty - && !tvdbId.IsWhiteSpace() - && int.TryParse(tvdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - item.SetProviderId(MetadataProvider.Tvdb, value.ToString(UsCulture)); - } + item.SetProviderId(MetadataProvider.Tvdb, tvdbRegex.Groups[1].Value); } } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index fbab8b521..c09781b1a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected override bool SupportsUrlAfterClosingXmlTag => true; /// - protected override string MovieDbParserSearchString => "themoviedb.org/tv/"; + protected override string TmdbRegex => "themoviedb\\.org\\/tv\\/([0-9]+)"; /// protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult itemResult) diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index ff4795569..7496784eb 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -152,6 +152,21 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal(id, item.ProviderIds[provider]); } + [Fact] + public void Parse_RadarrUrlFile_Success() + { + var result = new MetadataResult public SsdpDeviceLocator(ISsdpCommunicationsServer communicationsServer) { - if (communicationsServer == null) - { - throw new ArgumentNullException(nameof(communicationsServer)); - } - _CommunicationsServer = communicationsServer; - _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; + + if (communicationsServer != null) + { + // This can occur is dlna is enabled, but defined to run over https. + _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; + } _Devices = new List(); } From 2f843b3b4885aa6c0397c560bf11cada7d17c5aa Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 19:32:13 +0000 Subject: [PATCH 096/402] Hide msg if dlna disabled --- Emby.Dlna/Main/DlnaEntryPoint.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 9a2d524d1..d3e9a41ec 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -128,7 +128,8 @@ namespace Emby.Dlna.Main _netConfig = config.GetConfiguration("network"); _disabled = appHost.ListenWithHttps && _netConfig.RequireHttps; - if (_disabled) + + if (_disabled && _config.GetDlnaConfiguration().EnableServer) { _logger.LogError("The DLNA specification does not support HTTPS."); } From 1dd6036765f08892a39530ad73f5b046afbdaaaf Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 19:56:33 +0000 Subject: [PATCH 097/402] Fixed false starts --- Emby.Dlna/Ssdp/DeviceDiscovery.cs | 2 +- RSSDP/SsdpDeviceLocator.cs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index 8c7d961f3..d13871add 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs @@ -69,7 +69,7 @@ namespace Emby.Dlna.Ssdp { lock (_syncLock) { - if (_listenerCount > 0 && _deviceLocator == null) + if (_listenerCount > 0 && _deviceLocator == null && _commsServer != null) { _deviceLocator = new SsdpDeviceLocator(_commsServer); diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index e2b524cf4..9a110683e 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -27,14 +27,15 @@ namespace Rssdp.Infrastructure /// public SsdpDeviceLocator(ISsdpCommunicationsServer communicationsServer) { + if (communicationsServer == null) + { + throw new ArgumentNullException(nameof(communicationsServer)); + } + _CommunicationsServer = communicationsServer; - if (communicationsServer != null) - { - // This can occur is dlna is enabled, but defined to run over https. - _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; - } - + // This can occur is dlna is enabled, but defined to run over https. + _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; _Devices = new List(); } From a324d8a9c5351ec2f101f66e2f511eaf208f1254 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 19:57:32 +0000 Subject: [PATCH 098/402] removed space --- Jellyfin.sln | 15 ++++++++++----- RSSDP/SsdpDeviceLocator.cs | 5 ++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Jellyfin.sln b/Jellyfin.sln index 7b81f4346..3f8d8ce85 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30503.244 MinimumVisualStudioVersion = 10.0.40219.1 @@ -70,15 +71,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jell EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -210,6 +211,10 @@ Global {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index 9a110683e..0cdc5ce3d 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -31,11 +31,10 @@ namespace Rssdp.Infrastructure { throw new ArgumentNullException(nameof(communicationsServer)); } - - _CommunicationsServer = communicationsServer; - // This can occur is dlna is enabled, but defined to run over https. + _CommunicationsServer = communicationsServer; _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; + _Devices = new List(); } From 0960c945d04249e7ec4af2d7f74f53f1cfdd5116 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 19:58:45 +0000 Subject: [PATCH 099/402] Revert "removed space" This reverts commit a324d8a9c5351ec2f101f66e2f511eaf208f1254. --- Jellyfin.sln | 15 +++++---------- RSSDP/SsdpDeviceLocator.cs | 5 +++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Jellyfin.sln b/Jellyfin.sln index 3f8d8ce85..7b81f4346 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -1,5 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30503.244 MinimumVisualStudioVersion = 10.0.40219.1 @@ -71,15 +70,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jell EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -211,10 +210,6 @@ Global {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index 0cdc5ce3d..9a110683e 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -31,10 +31,11 @@ namespace Rssdp.Infrastructure { throw new ArgumentNullException(nameof(communicationsServer)); } - + _CommunicationsServer = communicationsServer; - _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; + // This can occur is dlna is enabled, but defined to run over https. + _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; _Devices = new List(); } From 5db998e1350ddd984c72624f882413c676bee6ef Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 19:59:18 +0000 Subject: [PATCH 100/402] removed space. --- RSSDP/SsdpDeviceLocator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index 9a110683e..31d520ff7 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -33,9 +33,8 @@ namespace Rssdp.Infrastructure } _CommunicationsServer = communicationsServer; - - // This can occur is dlna is enabled, but defined to run over https. _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; + _Devices = new List(); } From 1723ed1604a5ab6ae4d8e4dc243118c3267d879d Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 10 Mar 2021 19:59:58 +0000 Subject: [PATCH 101/402] removed the space AGAIN!!! --- RSSDP/SsdpDeviceLocator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index 31d520ff7..0cdc5ce3d 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -31,7 +31,7 @@ namespace Rssdp.Infrastructure { throw new ArgumentNullException(nameof(communicationsServer)); } - + _CommunicationsServer = communicationsServer; _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; From e6f49f50090f91c2c910066b3dbfe1ce1d2c0585 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 10 Mar 2021 21:00:33 +0100 Subject: [PATCH 102/402] Remove BuildPackage dependeny for PublishNuget in CI --- .ci/azure-pipelines-package.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml index 20f4dfe33..62018fd24 100644 --- a/.ci/azure-pipelines-package.yml +++ b/.ci/azure-pipelines-package.yml @@ -186,9 +186,6 @@ jobs: - job: PublishNuget displayName: 'Publish NuGet packages' - dependsOn: - - BuildPackage - condition: succeeded('BuildPackage') pool: vmImage: 'ubuntu-latest' From b827913fcf298bac28e5ced081dca5e4ded3f3ac Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 10 Mar 2021 21:09:45 +0100 Subject: [PATCH 103/402] Run CollectArtifacts taks regardless of result from BuildPackage and BuildDocker --- .ci/azure-pipelines-package.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml index 62018fd24..543fd7fc6 100644 --- a/.ci/azure-pipelines-package.yml +++ b/.ci/azure-pipelines-package.yml @@ -160,7 +160,6 @@ jobs: dependsOn: - BuildPackage - BuildDocker - condition: and(succeeded('BuildPackage'), succeeded('BuildDocker')) pool: vmImage: 'ubuntu-latest' From 8e09276d7d83304792dcbfc07ae511d42b90936d Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 10 Mar 2021 16:33:46 -0700 Subject: [PATCH 104/402] Add websocket session message type to generated openapi.json --- Jellyfin.Server/Filters/WebsocketModelFilter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Jellyfin.Server/Filters/WebsocketModelFilter.cs b/Jellyfin.Server/Filters/WebsocketModelFilter.cs index 248802857..38afb201d 100644 --- a/Jellyfin.Server/Filters/WebsocketModelFilter.cs +++ b/Jellyfin.Server/Filters/WebsocketModelFilter.cs @@ -25,6 +25,8 @@ namespace Jellyfin.Server.Filters context.SchemaGenerator.GenerateSchema(typeof(GeneralCommandType), context.SchemaRepository); context.SchemaGenerator.GenerateSchema(typeof(GroupUpdate), context.SchemaRepository); + + context.SchemaGenerator.GenerateSchema(typeof(SessionMessageType), context.SchemaRepository); } } } From e6407b414ba264ce277f3cf813fe5c2ca11c18ce Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Thu, 11 Mar 2021 09:34:10 -0500 Subject: [PATCH 105/402] Remove forum badge --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index cba88c8d2..6859a8a76 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,6 @@ Submit Feature Requests - -Discuss on our Forum - Chat on Matrix From 3fdf0de6e3b3600e2825b6ee75634e906d102ce2 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 11 Mar 2021 21:36:58 +0000 Subject: [PATCH 106/402] Fix remote access --- Jellyfin.Networking/Manager/NetworkManager.cs | 40 +++++++++++++++++++ .../IpBasedAccessValidationMiddleware.cs | 26 +----------- Jellyfin.sln | 17 +++++--- MediaBrowser.Common/Net/INetworkManager.cs | 7 ++++ .../NetworkParseTests.cs | 20 ++++++++++ 5 files changed, 79 insertions(+), 31 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index d2e9dcf9e..b874f5984 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -576,6 +576,46 @@ namespace Jellyfin.Networking.Manager return false; } + /// + /// Checks to see if has access. + /// + /// IP Address of client. + /// True if has access, otherwise false. + public bool HasRemoteAccess(IPAddress remoteIp) + { + var config = _configurationManager.GetNetworkConfiguration(); + if (config.EnableRemoteAccess) + { + // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. + // If left blank, all remote addresses will be allowed. + var remoteAddressFilter = RemoteAddressFilter; + + if (RemoteAddressFilter.Count > 0 && !IsInLocalNetwork(remoteIp)) + { + // remoteAddressFilter is a whitelist or blacklist. + bool isListed = RemoteAddressFilter.ContainsAddress(remoteIp); + if (config.IsRemoteIPFilterBlacklist) + { + // Black list, so flip over. + isListed = !isListed; + } + + if (!isListed) + { + // If your name isn't on the list, you arn't coming in. + return false; + } + } + } + else if (!IsInLocalNetwork(remoteIp)) + { + // Remote not enabled. So everyone should be LAN. + return false; + } + + return true; + } + /// /// Reloads all settings and re-initialises the instance. /// diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index 525cd9ffe..96e6fd834 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -42,32 +42,8 @@ namespace Jellyfin.Server.Middleware var remoteIp = httpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback; - if (serverConfigurationManager.GetNetworkConfiguration().EnableRemoteAccess) + if (!networkManager.HasRemoteAccess(remoteIp)) { - // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. - // If left blank, all remote addresses will be allowed. - var remoteAddressFilter = networkManager.RemoteAddressFilter; - - if (remoteAddressFilter.Count > 0 && !networkManager.IsInLocalNetwork(remoteIp)) - { - // remoteAddressFilter is a whitelist or blacklist. - bool isListed = remoteAddressFilter.ContainsAddress(remoteIp); - if (!serverConfigurationManager.GetNetworkConfiguration().IsRemoteIPFilterBlacklist) - { - // Black list, so flip over. - isListed = !isListed; - } - - if (!isListed) - { - // If your name isn't on the list, you arn't coming in. - return; - } - } - } - else if (!networkManager.IsInLocalNetwork(remoteIp)) - { - // Remote not enabled. So everyone should be LAN. return; } diff --git a/Jellyfin.sln b/Jellyfin.sln index 7b81f4346..9d5cd98e2 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30503.244 MinimumVisualStudioVersion = 10.0.40219.1 @@ -70,15 +71,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jell EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -210,6 +211,10 @@ Global {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index b6c390d23..012824f65 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -229,5 +229,12 @@ namespace MediaBrowser.Common.Net /// Optional filter for the list. /// Returns a filtered list of LAN addresses. Collection GetFilteredLANSubnets(Collection? filter = null); + + /// + /// Checks to see if has access. + /// + /// IP Address of client. + /// True if has access, otherwise false. + bool HasRemoteAccess(IPAddress remoteIp); } } diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index c3469035e..96b9f5e76 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -514,5 +514,25 @@ namespace Jellyfin.Networking.Tests Assert.Equal(intf, result); } + + [Theory] + [InlineData("185.10.10.10,200.200.200.200", "79.2.3.4", false, true)] // whitelist + [InlineData("185.10.10.10", "185.10.10.10", false, false)] // whitelist + [InlineData("185.10.10.10", "79.2.3.4", true, false)] // blacklist + public void TestRemoteAccess(string addresses, string remoteIp, bool blacklist, bool denied) + { + // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. + // If left blank, all remote addresses will be allowed. + var conf = new NetworkConfiguration() + { + EnableIPV4 = true, + RemoteIPFilter = addresses.Split(","), + IsRemoteIPFilterBlacklist = blacklist + }; + + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIp)), denied); + } } } From 034ee38583e613e7dd24282d02051f599b15cedc Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 11 Mar 2021 21:42:01 +0000 Subject: [PATCH 107/402] Update Jellyfin.sln reverted --- Jellyfin.sln | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Jellyfin.sln b/Jellyfin.sln index 9d5cd98e2..7b81f4346 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -1,5 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30503.244 MinimumVisualStudioVersion = 10.0.40219.1 @@ -71,15 +70,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jell EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -211,10 +210,6 @@ Global {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU From f66cb9777ddaf42f19ea452f85c5fa0b7e907391 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 11 Mar 2021 22:46:07 +0000 Subject: [PATCH 108/402] Update Jellyfin.Networking/Manager/NetworkManager.cs Co-authored-by: Claus Vium --- Jellyfin.Networking/Manager/NetworkManager.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index b874f5984..f42c0aeeb 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -593,18 +593,7 @@ namespace Jellyfin.Networking.Manager if (RemoteAddressFilter.Count > 0 && !IsInLocalNetwork(remoteIp)) { // remoteAddressFilter is a whitelist or blacklist. - bool isListed = RemoteAddressFilter.ContainsAddress(remoteIp); - if (config.IsRemoteIPFilterBlacklist) - { - // Black list, so flip over. - isListed = !isListed; - } - - if (!isListed) - { - // If your name isn't on the list, you arn't coming in. - return false; - } + return RemoteAddressFilter.ContainsAddress(remoteIp) == !config.IsRemoteIPFilterBlacklist; } } else if (!IsInLocalNetwork(remoteIp)) From 3fa84500cf7430e3f56f04303e7e150f10e88dfb Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 11 Mar 2021 22:46:24 +0000 Subject: [PATCH 109/402] Update Jellyfin.Networking/Manager/NetworkManager.cs Co-authored-by: Claus Vium --- Jellyfin.Networking/Manager/NetworkManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index f42c0aeeb..b0046af48 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -588,8 +588,6 @@ namespace Jellyfin.Networking.Manager { // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. // If left blank, all remote addresses will be allowed. - var remoteAddressFilter = RemoteAddressFilter; - if (RemoteAddressFilter.Count > 0 && !IsInLocalNetwork(remoteIp)) { // remoteAddressFilter is a whitelist or blacklist. From e5914fd28c9f5073eeadff6fc6097297837b2544 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 11 Mar 2021 22:47:30 +0000 Subject: [PATCH 110/402] split tests --- .../NetworkParseTests.cs | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 96b9f5e76..8993d487a 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -516,10 +516,9 @@ namespace Jellyfin.Networking.Tests } [Theory] - [InlineData("185.10.10.10,200.200.200.200", "79.2.3.4", false, true)] // whitelist - [InlineData("185.10.10.10", "185.10.10.10", false, false)] // whitelist - [InlineData("185.10.10.10", "79.2.3.4", true, false)] // blacklist - public void TestRemoteAccess(string addresses, string remoteIp, bool blacklist, bool denied) + [InlineData("185.10.10.10,200.200.200.200", "79.2.3.4", true)] + [InlineData("185.10.10.10", "185.10.10.10", false)] + public void HasRemoteAccess_GivenNonEmptyWhitelist_AllowsOnlyIpsInWhitelist(string addresses, string remoteIp, bool denied) { // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. // If left blank, all remote addresses will be allowed. @@ -527,7 +526,25 @@ namespace Jellyfin.Networking.Tests { EnableIPV4 = true, RemoteIPFilter = addresses.Split(","), - IsRemoteIPFilterBlacklist = blacklist + IsRemoteIPFilterBlacklist = false + }; + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIp)), denied); + } + + [Theory] + [InlineData("185.10.10.10", "79.2.3.4", false)] // blacklist + [InlineData("185.10.10.10", "185.10.10.10", true)] // blacklist + public void HasRemoteAccess_GivenNonEmptBlacklist_BlacklistTheIps(string addresses, string remoteIp, bool denied) + { + // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. + // If left blank, all remote addresses will be allowed. + var conf = new NetworkConfiguration() + { + EnableIPV4 = true, + RemoteIPFilter = addresses.Split(","), + IsRemoteIPFilterBlacklist = true }; using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); From eef15dc7ac23f026f11334e2a262a572df126e48 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 11 Mar 2021 22:45:58 -0700 Subject: [PATCH 111/402] Fix third part integration --- Jellyfin.Api/Controllers/LibraryController.cs | 6 ++-- .../Controllers/NotificationsController.cs | 21 +++++-------- .../Models/LibraryDtos/MediaUpdateInfoDto.cs | 15 ++++------ .../LibraryDtos/MediaUpdateInfoPathDto.cs | 19 ++++++++++++ .../NotificationDtos/AdminNotificationDto.cs | 30 +++++++++++++++++++ 5 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs create mode 100644 Jellyfin.Api/Models/NotificationDtos/AdminNotificationDto.cs diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 3443ebd72..2bd2d4933 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -590,15 +590,15 @@ namespace Jellyfin.Api.Controllers /// /// Reports that new movies have been added by an external source. /// - /// A list of updated media paths. + /// The update paths. /// Report success. /// A . [HttpPost("Library/Media/Updated")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult PostUpdatedMedia([FromBody, Required] MediaUpdateInfoDto[] updates) + public ActionResult PostUpdatedMedia([FromBody, Required] MediaUpdateInfoDto dto) { - foreach (var item in updates) + foreach (var item in dto.Updates) { _libraryMonitor.ReportFileSystemChanged(item.Path); } diff --git a/Jellyfin.Api/Controllers/NotificationsController.cs b/Jellyfin.Api/Controllers/NotificationsController.cs index 0ceda6815..420630cdf 100644 --- a/Jellyfin.Api/Controllers/NotificationsController.cs +++ b/Jellyfin.Api/Controllers/NotificationsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; using Jellyfin.Api.Constants; @@ -86,26 +87,19 @@ namespace Jellyfin.Api.Controllers /// /// Sends a notification to all admins. /// - /// The URL of the notification. - /// The level of the notification. - /// The name of the notification. - /// The description of the notification. + /// The notification request. /// Notification sent. /// A . [HttpPost("Admin")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult CreateAdminNotification( - [FromQuery] string? url, - [FromQuery] NotificationLevel? level, - [FromQuery] string name = "", - [FromQuery] string description = "") + public ActionResult CreateAdminNotification([FromBody, Required] AdminNotificationDto notificationDto) { var notification = new NotificationRequest { - Name = name, - Description = description, - Url = url, - Level = level ?? NotificationLevel.Normal, + Name = notificationDto.Name, + Description = notificationDto.Description, + Url = notificationDto.Url, + Level = notificationDto.NotificationLevel ?? NotificationLevel.Normal, UserIds = _userManager.Users .Where(user => user.HasPermission(PermissionKind.IsAdministrator)) .Select(user => user.Id) @@ -114,7 +108,6 @@ namespace Jellyfin.Api.Controllers }; _notificationManager.SendNotification(notification, CancellationToken.None); - return NoContent(); } diff --git a/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs index 991dbfc50..f93638898 100644 --- a/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs +++ b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoDto.cs @@ -1,4 +1,7 @@ -namespace Jellyfin.Api.Models.LibraryDtos +using System; +using System.Collections.Generic; + +namespace Jellyfin.Api.Models.LibraryDtos { /// /// Media Update Info Dto. @@ -6,14 +9,8 @@ public class MediaUpdateInfoDto { /// - /// Gets or sets media path. + /// Gets or sets the list of updates. /// - public string? Path { get; set; } - - /// - /// Gets or sets media update type. - /// Created, Modified, Deleted. - /// - public string? UpdateType { get; set; } + public IReadOnlyList Updates { get; set; } = Array.Empty(); } } diff --git a/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs new file mode 100644 index 000000000..852315b92 --- /dev/null +++ b/Jellyfin.Api/Models/LibraryDtos/MediaUpdateInfoPathDto.cs @@ -0,0 +1,19 @@ +namespace Jellyfin.Api.Models.LibraryDtos +{ + /// + /// The media update info path. + /// + public class MediaUpdateInfoPathDto + { + /// + /// Gets or sets media path. + /// + public string? Path { get; set; } + + /// + /// Gets or sets media update type. + /// Created, Modified, Deleted. + /// + public string? UpdateType { get; set; } + } +} diff --git a/Jellyfin.Api/Models/NotificationDtos/AdminNotificationDto.cs b/Jellyfin.Api/Models/NotificationDtos/AdminNotificationDto.cs new file mode 100644 index 000000000..2c3a6282f --- /dev/null +++ b/Jellyfin.Api/Models/NotificationDtos/AdminNotificationDto.cs @@ -0,0 +1,30 @@ +using MediaBrowser.Model.Notifications; + +namespace Jellyfin.Api.Models.NotificationDtos +{ + /// + /// The admin notification dto. + /// + public class AdminNotificationDto + { + /// + /// Gets or sets the notification name. + /// + public string? Name { get; set; } + + /// + /// Gets or sets the notification description. + /// + public string? Description { get; set; } + + /// + /// Gets or sets the notification level. + /// + public NotificationLevel? NotificationLevel { get; set; } + + /// + /// Gets or sets the notification url. + /// + public string? Url { get; set; } + } +} From 80846a1c66eb7f669d95d021786b5273ba3b15f6 Mon Sep 17 00:00:00 2001 From: WWWesten Date: Fri, 12 Mar 2021 12:58:05 +0000 Subject: [PATCH 112/402] Translated using Weblate (Kazakh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kk/ --- Emby.Server.Implementations/Localization/Core/kk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index bdfb786c9..829a29ad4 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -116,7 +116,7 @@ "TaskRefreshPeopleDescription": "Tasyğyşhanadağy aktörler men rejisörler metaderekterın jaŋartady.", "TaskCleanLogsDescription": "{0} künnen asqan jūrnal faildaryn joiady.", "TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaŋa faildardy skanerleidі jäne metaderekterdı jaŋğyrtady.", - "TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşіn nobailar jasaidy.", + "TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşın nobailar jasaidy.", "TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.", "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teŋşelgen jasynan asqan jazbalary joiady." } From e814d8e2cfe41acb9269c048bbbd5f133dfcae7b Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 12 Mar 2021 06:43:57 -0700 Subject: [PATCH 113/402] Add JsonStringConverter --- .../Json/Converters/JsonStringConverter.cs | 38 ++++++++++++++++++ MediaBrowser.Common/Json/JsonDefaults.cs | 3 +- .../Json/JsonStringConverterTests.cs | 39 +++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 MediaBrowser.Common/Json/Converters/JsonStringConverter.cs create mode 100644 tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs new file mode 100644 index 000000000..585f52b8f --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs @@ -0,0 +1,38 @@ +using System; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Converter to allow the serializer to read strings. + /// + public class JsonStringConverter : JsonConverter + { + /// + public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.Null => null, + JsonTokenType.String => reader.GetString(), + _ => GetRawValue(reader) + }; + } + + /// + public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + { + writer.WriteStringValue(value); + } + + private static string GetRawValue(Utf8JsonReader reader) + { + var utf8Bytes = reader.HasValueSequence + ? reader.ValueSequence.FirstSpan + : reader.ValueSpan; + return Encoding.UTF8.GetString(utf8Bytes); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 2ef24a884..61938fdf2 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -39,7 +39,8 @@ namespace MediaBrowser.Common.Json new JsonStringEnumConverter(), new JsonNullableStructConverterFactory(), new JsonBoolNumberConverter(), - new JsonDateTimeConverter() + new JsonDateTimeConverter(), + new JsonStringConverter() } }; diff --git a/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs new file mode 100644 index 000000000..fd77694b3 --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs @@ -0,0 +1,39 @@ +using System.Text.Json; +using MediaBrowser.Common.Json.Converters; +using Xunit; + +namespace Jellyfin.Common.Tests.Json +{ + public class JsonStringConverterTests + { + private readonly JsonSerializerOptions _jsonSerializerOptions + = new () + { + Converters = + { + new JsonStringConverter() + } + }; + + [Theory] + [InlineData("\"test\"", "test")] + [InlineData("123", "123")] + [InlineData("123.45", "123.45")] + [InlineData("true", "true")] + [InlineData("false", "false")] + public void Deserialize_String_Valid_Success(string input, string output) + { + var deserialized = JsonSerializer.Deserialize(input, _jsonSerializerOptions); + Assert.Equal(deserialized, output); + } + + [Fact] + public void Deserialize_Int32asInt32_Valid_Success() + { + const string? input = "123"; + const int output = 123; + var deserialized = JsonSerializer.Deserialize(input, _jsonSerializerOptions); + Assert.Equal(deserialized, output); + } + } +} \ No newline at end of file From b7fb152faf6c01e20d9b8c46be13fc7cc5e0edbc Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Fri, 12 Mar 2021 14:44:05 +0000 Subject: [PATCH 114/402] Update tests/Jellyfin.Networking.Tests/NetworkParseTests.cs Co-authored-by: Claus Vium --- tests/Jellyfin.Networking.Tests/NetworkParseTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 8993d487a..19f0faa8f 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -536,7 +536,7 @@ namespace Jellyfin.Networking.Tests [Theory] [InlineData("185.10.10.10", "79.2.3.4", false)] // blacklist [InlineData("185.10.10.10", "185.10.10.10", true)] // blacklist - public void HasRemoteAccess_GivenNonEmptBlacklist_BlacklistTheIps(string addresses, string remoteIp, bool denied) + public void HasRemoteAccess_GivenNonEmptyBlacklist_BlacklistTheIps(string addresses, string remoteIp, bool denied) { // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. // If left blank, all remote addresses will be allowed. From 4cf88f67baf8f76ce4ed630cc73ef73405c0212b Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Fri, 12 Mar 2021 14:58:04 +0000 Subject: [PATCH 115/402] Update NetworkManager.cs --- Jellyfin.Networking/Manager/NetworkManager.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index b0046af48..785a058d3 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -576,11 +576,7 @@ namespace Jellyfin.Networking.Manager return false; } - /// - /// Checks to see if has access. - /// - /// IP Address of client. - /// True if has access, otherwise false. + /// public bool HasRemoteAccess(IPAddress remoteIp) { var config = _configurationManager.GetNetworkConfiguration(); From 1d5b7b61fb002dd4b2e3fe4cf8dd02df590f64e7 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Fri, 12 Mar 2021 22:20:13 +0000 Subject: [PATCH 116/402] Change First to FirstOrDefault --- Emby.Dlna/DlnaManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index d7b75f979..3f4925660 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -333,7 +333,12 @@ namespace Emby.Dlna throw new ArgumentNullException(nameof(id)); } - var info = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, id, StringComparison.OrdinalIgnoreCase)); + var info = GetProfileInfosInternal().FirstOrDefault(i => string.Equals(i.Info.Id, id, StringComparison.OrdinalIgnoreCase)); + + if (info == null) + { + return null; + } return ParseProfileFile(info.Path, info.Info.Type); } From 500832bdfd3dbbb5367484561079b579cb853318 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 12 Mar 2021 17:11:43 -0700 Subject: [PATCH 117/402] Set openapi version to server version --- Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 1828f1a7e..9670a2b2c 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -260,15 +260,16 @@ namespace Jellyfin.Server.Extensions { return serviceCollection.AddSwaggerGen(c => { + var version = typeof(ApplicationHost).Assembly.GetName().Version?.ToString(); c.SwaggerDoc("api-docs", new OpenApiInfo { Title = "Jellyfin API", - Version = "v1", + Version = version, Extensions = new Dictionary { { "x-jellyfin-version", - new OpenApiString(typeof(ApplicationHost).Assembly.GetName().Version?.ToString()) + new OpenApiString(version) } } }); From fe2a310fe28bb893a855d1278845ce6539c39656 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sat, 13 Mar 2021 09:13:45 +0100 Subject: [PATCH 118/402] fix refresh endpoint It was originally a POST https://github.com/jellyfin/jellyfin/blob/9af6eda0b495649e3a77694b2bb30abad1a26484/MediaBrowser.Api/Library/LibraryService.cs#L155 --- Jellyfin.Api/Controllers/LibraryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 3443ebd72..76f882b43 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -303,7 +303,7 @@ namespace Jellyfin.Api.Controllers /// /// Library scan started. /// A . - [HttpGet("Library/Refresh")] + [HttpPost("Library/Refresh")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task RefreshLibrary() From 37b1b31a46e818cb5d229ebf11d7db7895c3b964 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 13 Mar 2021 08:41:16 -0700 Subject: [PATCH 119/402] Convert full ValueSequence --- MediaBrowser.Common/Json/Converters/JsonStringConverter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs index 585f52b8f..669b3cd07 100644 --- a/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -30,7 +31,7 @@ namespace MediaBrowser.Common.Json.Converters private static string GetRawValue(Utf8JsonReader reader) { var utf8Bytes = reader.HasValueSequence - ? reader.ValueSequence.FirstSpan + ? reader.ValueSequence.ToArray() : reader.ValueSpan; return Encoding.UTF8.GetString(utf8Bytes); } From 1169a0214b3ed166e53b9d6a80c8cbf707518e7b Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 13 Mar 2021 08:43:14 -0700 Subject: [PATCH 120/402] Set default version --- Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 9670a2b2c..c2b3d9f5e 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -260,7 +260,7 @@ namespace Jellyfin.Server.Extensions { return serviceCollection.AddSwaggerGen(c => { - var version = typeof(ApplicationHost).Assembly.GetName().Version?.ToString(); + var version = typeof(ApplicationHost).Assembly.GetName().Version?.ToString() ?? "0.0.0.1"; c.SwaggerDoc("api-docs", new OpenApiInfo { Title = "Jellyfin API", From 9ac9543ee2b69f423f0102f80671fb09893534ae Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 13 Mar 2021 09:09:22 -0700 Subject: [PATCH 121/402] Add missing InstantMix endpoints --- .../Controllers/InstantMixController.cs | 90 +++++++++++++++++-- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index f061755c3..f232dffaa 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -86,7 +86,7 @@ namespace Jellyfin.Api.Controllers } /// - /// Creates an instant playlist based on a given song. + /// Creates an instant playlist based on a given album. /// /// The item id. /// Optional. Filter by user id, and attach user data. @@ -122,7 +122,7 @@ namespace Jellyfin.Api.Controllers } /// - /// Creates an instant playlist based on a given song. + /// Creates an instant playlist based on a given playlist. /// /// The item id. /// Optional. Filter by user id, and attach user data. @@ -158,7 +158,7 @@ namespace Jellyfin.Api.Controllers } /// - /// Creates an instant playlist based on a given song. + /// Creates an instant playlist based on a given genre. /// /// The genre name. /// Optional. Filter by user id, and attach user data. @@ -172,7 +172,7 @@ namespace Jellyfin.Api.Controllers /// A with the playlist items. [HttpGet("MusicGenres/{name}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetInstantMixFromMusicGenre( + public ActionResult> GetInstantMixFromMusicGenreByName( [FromRoute, Required] string name, [FromQuery] Guid? userId, [FromQuery] int? limit, @@ -193,7 +193,7 @@ namespace Jellyfin.Api.Controllers } /// - /// Creates an instant playlist based on a given song. + /// Creates an instant playlist based on a given artist. /// /// The item id. /// Optional. Filter by user id, and attach user data. @@ -229,7 +229,7 @@ namespace Jellyfin.Api.Controllers } /// - /// Creates an instant playlist based on a given song. + /// Creates an instant playlist based on a given genre. /// /// The item id. /// Optional. Filter by user id, and attach user data. @@ -243,7 +243,7 @@ namespace Jellyfin.Api.Controllers /// A with the playlist items. [HttpGet("MusicGenres/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetInstantMixFromMusicGenres( + public ActionResult> GetInstantMixFromMusicGenreById( [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, @@ -265,7 +265,7 @@ namespace Jellyfin.Api.Controllers } /// - /// Creates an instant playlist based on a given song. + /// Creates an instant playlist based on a given item. /// /// The item id. /// Optional. Filter by user id, and attach user data. @@ -300,6 +300,80 @@ namespace Jellyfin.Api.Controllers return GetResult(items, user, limit, dtoOptions); } + /// + /// Creates an instant playlist based on a given artist. + /// + /// The item id. + /// Optional. Filter by user id, and attach user data. + /// Optional. The maximum number of records to return. + /// Optional. Specify additional fields of information to return in the output. + /// Optional. Include image information in output. + /// Optional. Include user data. + /// Optional. The max number of images to return, per image type. + /// Optional. The image types to include in the output. + /// Instant playlist returned. + /// A with the playlist items. + [HttpGet("Artists/InstantMix")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Obsolete("Use GetInstantMixFromArtists")] + public ActionResult> GetInstantMixFromArtists2( + [FromQuery, Required] Guid id, + [FromQuery] Guid? userId, + [FromQuery] int? limit, + [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, + [FromQuery] bool? enableImages, + [FromQuery] bool? enableUserData, + [FromQuery] int? imageTypeLimit, + [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) + { + return GetInstantMixFromArtists( + id, + userId, + limit, + fields, + enableImages, + enableUserData, + imageTypeLimit, + enableImageTypes); + } + + /// + /// Creates an instant playlist based on a given genre. + /// + /// The item id. + /// Optional. Filter by user id, and attach user data. + /// Optional. The maximum number of records to return. + /// Optional. Specify additional fields of information to return in the output. + /// Optional. Include image information in output. + /// Optional. Include user data. + /// Optional. The max number of images to return, per image type. + /// Optional. The image types to include in the output. + /// Instant playlist returned. + /// A with the playlist items. + [HttpGet("MusicGenres/InstantMix")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Obsolete("Use GetInstantMixFromMusicGenres instead")] + public ActionResult> GetInstantMixFromMusicGenreById2( + [FromQuery, Required] Guid id, + [FromQuery] Guid? userId, + [FromQuery] int? limit, + [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, + [FromQuery] bool? enableImages, + [FromQuery] bool? enableUserData, + [FromQuery] int? imageTypeLimit, + [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) + { + return GetInstantMixFromMusicGenreById( + id, + userId, + limit, + fields, + enableImages, + enableUserData, + imageTypeLimit, + enableImageTypes); + } + private QueryResult GetResult(List items, User? user, int? limit, DtoOptions dtoOptions) { var list = items; From f5789483fd5288fe735057bbf6e38a260e6915d2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Mar 2021 19:18:11 +0100 Subject: [PATCH 122/402] Add test for HdHomerunManager.WriteSetMessage --- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 2 +- .../LiveTv/HdHomerunManagerTests.cs | 37 ++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 20a4d87fb..579333254 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -292,7 +292,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return FinishPacket(buffer, offset); } - private static int WriteSetMessage(Span buffer, int tuner, string name, string value, uint? lockkey) + internal static int WriteSetMessage(Span buffer, int tuner, string name, string value, uint? lockkey) { var byteName = string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}", tuner, name); int offset = WriteHeaderAndPayload(buffer, byteName); diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs index 7e04a1ec1..e8634be4c 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs @@ -17,8 +17,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Span buffer = stackalloc byte[128]; int len = HdHomerunManager.WriteNullTerminatedString(buffer, string.Empty); - Assert.Equal(expected.Length, len); - Assert.True(expected.SequenceEqual(buffer.Slice(0, len))); + Assert.Equal( + Convert.ToHexString(expected), + Convert.ToHexString(buffer.Slice(0, len))); } [Fact] @@ -32,8 +33,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Span buffer = stackalloc byte[128]; int len = HdHomerunManager.WriteNullTerminatedString(buffer, "The quick"); - Assert.Equal(expected.Length, len); - Assert.True(expected.SequenceEqual(buffer.Slice(0, len))); + Assert.Equal( + Convert.ToHexString(expected), + Convert.ToHexString(buffer.Slice(0, len))); } [Fact] @@ -51,8 +53,31 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Span buffer = stackalloc byte[128]; int len = HdHomerunManager.WriteGetMessage(buffer, 0, "N"); - Assert.Equal(expected.Length, len); - Assert.True(expected.SequenceEqual(buffer.Slice(0, len))); + Assert.Equal( + Convert.ToHexString(expected), + Convert.ToHexString(buffer.Slice(0, len))); + } + + [Fact] + public void WriteSetMessage_NoLockKey_Success() + { + ReadOnlySpan expected = stackalloc byte[] + { + 0, 4, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0xa9, 0x49, 0xd0, 0x68 + }; + + Span buffer = stackalloc byte[128]; + int len = HdHomerunManager.WriteSetMessage(buffer, 0, "N", "value", null); + + Assert.Equal( + Convert.ToHexString(expected), + Convert.ToHexString(buffer.Slice(0, len))); } } } From 4b17648df348ac17b359e2dcfe3e361798d45f2b Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 13 Mar 2021 19:23:52 +0100 Subject: [PATCH 123/402] Remove empty line Co-authored-by: Cody Robibero --- Emby.Server.Implementations/Collections/CollectionManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 449f8872f..907b58886 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -347,7 +347,6 @@ namespace Emby.Server.Implementations.Collections var alreadyInResults = false; foreach (var child in item.GetMediaSources(true)) { - if (results.ContainsKey(Guid.Parse(child.Id))) { alreadyInResults = true; From 7a3109104bf4154d285c94a48504083368556c55 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 13 Mar 2021 19:24:02 +0100 Subject: [PATCH 124/402] Remove empty line Co-authored-by: Cody Robibero --- Emby.Server.Implementations/Collections/CollectionManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 907b58886..151e86948 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -354,7 +354,6 @@ namespace Emby.Server.Implementations.Collections } if (!alreadyInResults) { - results[item.Id] = item; } } From 7fb3a354fd6dd8fa83832566f76bcf0a46a5e82f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Mar 2021 19:24:47 +0100 Subject: [PATCH 125/402] Add test for HdHomerunManager.WriteSetMessage with lockkey --- .../LiveTv/HdHomerunManagerTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs index e8634be4c..2991fe1ed 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs @@ -79,5 +79,29 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Convert.ToHexString(expected), Convert.ToHexString(buffer.Slice(0, len))); } + + [Fact] + public void WriteSetMessage_LockKey_Success() + { + ReadOnlySpan expected = stackalloc byte[] + { + 0, 4, + 0, 26, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 21, + 4, 0x00, 0x01, 0x38, 0xd5, + 0x8e, 0xb6, 0x06, 0x82 + }; + + Span buffer = stackalloc byte[128]; + int len = HdHomerunManager.WriteSetMessage(buffer, 0, "N", "value", 80085); + + Assert.Equal( + Convert.ToHexString(expected), + Convert.ToHexString(buffer.Slice(0, len))); + } } } From e8b18e5f8fe87f981b11237d24032ab11589bcd2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Mar 2021 19:32:40 +0100 Subject: [PATCH 126/402] Add test for HdHomerunManager.ParseReturnMessage --- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 2 +- .../LiveTv/HdHomerunManagerTests.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 579333254..9c113bc7c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -355,7 +355,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return offset + 4; } - private static bool ParseReturnMessage(byte[] buf, int numBytes, out string returnVal) + internal static bool ParseReturnMessage(byte[] buf, int numBytes, out string returnVal) { returnVal = string.Empty; diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs index 2991fe1ed..355cfa641 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs @@ -103,5 +103,23 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Convert.ToHexString(expected), Convert.ToHexString(buffer.Slice(0, len))); } + + [Fact] + public void ParseReturnMessage_Valid_Success() + { + ReadOnlySpan packet = stackalloc byte[] + { + 0, 5, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x7d, 0xa3, 0xa3, 0xf3 + }; + + Assert.True(HdHomerunManager.ParseReturnMessage(packet.ToArray(), packet.Length, out var value)); + Assert.Equal("value", value); + } } } From 4cc3b938fa7913bccf7229dafce4b9f1d369dd16 Mon Sep 17 00:00:00 2001 From: Mister Rajoy Date: Sat, 13 Mar 2021 20:33:05 +0100 Subject: [PATCH 127/402] Change Guid.Parse to Guid.TryParse --- Emby.Server.Implementations/Collections/CollectionManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 449f8872f..b5d95134b 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -348,11 +348,13 @@ namespace Emby.Server.Implementations.Collections foreach (var child in item.GetMediaSources(true)) { - if (results.ContainsKey(Guid.Parse(child.Id))) + if (Guid.TryParse(child.Id, out var id) && results.ContainsKey(id)) { alreadyInResults = true; + break; } } + if (!alreadyInResults) { From f9640f43663fdf443c130f61cab3521fcb1c7823 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Mar 2021 21:12:11 +0100 Subject: [PATCH 128/402] Rewrite HdHomerunManager.ParseReturnMessage --- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 85 +++---- .../LiveTv/HdHomerunManagerTests.cs | 209 +++++++++++++++++- 2 files changed, 249 insertions(+), 45 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 9c113bc7c..a7fda1d72 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -130,9 +130,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); - ParseReturnMessage(buffer, receivedBytes, out string returnVal); - - return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase); + return VerifyReturnValueOfGetSet(buffer.AsSpan(receivedBytes), "none"); } finally { @@ -173,7 +171,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out _)) + if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _)) { continue; } @@ -185,7 +183,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out _)) + if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _)) { await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false); continue; @@ -199,7 +197,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out _)) + if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _)) { await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false); continue; @@ -239,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); // parse response to make sure it worked - if (!ParseReturnMessage(buffer, receivedBytes, out _)) + if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _)) { return; } @@ -355,60 +353,65 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return offset + 4; } - internal static bool ParseReturnMessage(byte[] buf, int numBytes, out string returnVal) + internal static bool VerifyReturnValueOfGetSet(ReadOnlySpan buffer, string expected) { - returnVal = string.Empty; + return TryGetReturnValueOfGetSet(buffer, out var value) + && string.Equals(Encoding.UTF8.GetString(value), expected, StringComparison.OrdinalIgnoreCase); + } - if (numBytes < 4) + internal static bool TryGetReturnValueOfGetSet(ReadOnlySpan buffer, out ReadOnlySpan value) + { + value = ReadOnlySpan.Empty; + + if (buffer.Length < 8) { return false; } - var flipEndian = BitConverter.IsLittleEndian; - int offset = 0; - byte[] msgTypeBytes = new byte[2]; - Buffer.BlockCopy(buf, offset, msgTypeBytes, 0, msgTypeBytes.Length); - - if (flipEndian) - { - Array.Reverse(msgTypeBytes); - } - - var msgType = BitConverter.ToUInt16(msgTypeBytes, 0); - offset += 2; - - if (msgType != GetSetReply) + uint crc = BinaryPrimitives.ReadUInt32LittleEndian(buffer[^4..]); + if (crc != Crc32.Compute(buffer[..^4])) { return false; } - byte[] msgLengthBytes = new byte[2]; - Buffer.BlockCopy(buf, offset, msgLengthBytes, 0, msgLengthBytes.Length); - if (flipEndian) - { - Array.Reverse(msgLengthBytes); - } - - var msgLength = BitConverter.ToUInt16(msgLengthBytes, 0); - offset += 2; - - if (numBytes < msgLength + 8) + if (BinaryPrimitives.ReadUInt16BigEndian(buffer) != GetSetReply) { return false; } - offset++; // Name Tag + var msgLength = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(2)); + if (buffer.Length != 2 + 2 + 4 + msgLength) + { + return false; + } - var nameLength = buf[offset++]; + var offset = 4; + if (buffer[offset++] != GetSetName) + { + return false; + } + + var nameLength = buffer[offset++]; + if (buffer.Length < 4 + 1 + offset + nameLength) + { + return false; + } - // skip the name field to get to value for return offset += nameLength; - offset++; // Value Tag + if (buffer[offset++] != GetSetValue) + { + return false; + } - var valueLength = buf[offset++]; + var valueLength = buffer[offset++]; + if (buffer.Length < 4 + offset + valueLength) + { + return false; + } - returnVal = Encoding.UTF8.GetString(buf, offset, valueLength - 1); // remove null terminator + // remove null terminator + value = buffer.Slice(offset, valueLength - 1); return true; } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs index 355cfa641..c404e6ed5 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun; using Xunit; @@ -105,9 +106,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv } [Fact] - public void ParseReturnMessage_Valid_Success() + public void TryGetReturnValueOfGetSet_Valid_Success() { - ReadOnlySpan packet = stackalloc byte[] + ReadOnlySpan packet = new byte[] { 0, 5, 0, 20, @@ -118,8 +119,208 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv 0x7d, 0xa3, 0xa3, 0xf3 }; - Assert.True(HdHomerunManager.ParseReturnMessage(packet.ToArray(), packet.Length, out var value)); - Assert.Equal("value", value); + Assert.True(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out var value)); + Assert.Equal("value", Encoding.UTF8.GetString(value)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_InvalidPacketType_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 4, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x7d, 0xa3, 0xa3, 0xf3 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_InvalidCrc_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x7d, 0xa3, 0xa3, 0xf4 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_InvalidPacket_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 0x7d, 0xa3, 0xa3 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_TooSmallMessageLength_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 19, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x25, 0x25, 0x44, 0x9a + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_TooLargeMessageLength_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 21, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0xe3, 0x20, 0x79, 0x6c + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_TooLargeNameLength_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 3, + 20, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0xe1, 0x8e, 0x9c, 0x74 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_InvalidGetSetNameTag_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 4, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0xee, 0x05, 0xe7, 0x12 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_InvalidGetSetValueTag_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 3, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x64, 0xaa, 0x66, 0xf9 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void TryGetReturnValueOfGetSet_TooLargeValueLength_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 7, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0xc9, 0xa8, 0xd4, 0x55 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + + [Fact] + public void VerifyReturnValueOfGetSet_Valid_True() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x7d, 0xa3, 0xa3, 0xf3 + }; + + Assert.True(HdHomerunManager.VerifyReturnValueOfGetSet(packet, "value")); + } + + [Fact] + public void VerifyReturnValueOfGetSet_WrongValue_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 5, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x7d, 0xa3, 0xa3, 0xf3 + }; + + Assert.False(HdHomerunManager.VerifyReturnValueOfGetSet(packet, "none")); + } + + [Fact] + public void VerifyReturnValueOfGetSet_InvalidPacket_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 4, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0x7d, 0xa3, 0xa3, 0xf3 + }; + + Assert.False(HdHomerunManager.VerifyReturnValueOfGetSet(packet, "value")); } } } From d9eb7ae6dc69aa4d0b77e7a2ae1c9ccac78d0db9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Mar 2021 21:28:04 +0100 Subject: [PATCH 129/402] Fix invalid crc in TryGetReturnValueOfGetSet_InvalidPacketType test --- .../LiveTv/HdHomerunManagerTests.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs index c404e6ed5..fd499d9cf 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs @@ -123,23 +123,6 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Assert.Equal("value", Encoding.UTF8.GetString(value)); } - [Fact] - public void TryGetReturnValueOfGetSet_InvalidPacketType_False() - { - ReadOnlySpan packet = new byte[] - { - 0, 4, - 0, 20, - 3, - 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, - 4, - 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, - 0x7d, 0xa3, 0xa3, 0xf3 - }; - - Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); - } - [Fact] public void TryGetReturnValueOfGetSet_InvalidCrc_False() { @@ -157,6 +140,23 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); } + [Fact] + public void TryGetReturnValueOfGetSet_InvalidPacketType_False() + { + ReadOnlySpan packet = new byte[] + { + 0, 4, + 0, 20, + 3, + 10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0, + 4, + 6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0, + 0xa9, 0x49, 0xd0, 0x68 + }; + + Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _)); + } + [Fact] public void TryGetReturnValueOfGetSet_InvalidPacket_False() { From a8ed753f6c890f74d3a70c2653ac5548d2399737 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 9 Mar 2021 05:57:38 +0100 Subject: [PATCH 130/402] FxCop -> Net Analyzers (part 2) --- Emby.Dlna/DlnaManager.cs | 2 +- .../ApplicationHost.cs | 5 +- .../Channels/ChannelManager.cs | 2 +- .../Data/SqliteItemRepository.cs | 2 +- .../HttpServer/WebSocketConnection.cs | 2 +- .../Library/LiveStreamHelper.cs | 2 +- .../Library/MediaSourceManager.cs | 2 +- .../LiveTv/EmbyTV/EncodedRecorder.cs | 2 +- .../LiveTv/EmbyTV/ItemDataProvider.cs | 2 +- .../LiveTv/Listings/SchedulesDirect.cs | 2 +- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 2 +- .../Localization/LocalizationManager.cs | 2 +- .../Plugins/PluginManager.cs | 4 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 2 +- .../Updates/InstallationManager.cs | 2 +- .../Controllers/ConfigurationController.cs | 2 +- Jellyfin.Api/Controllers/PluginsController.cs | 2 +- Jellyfin.Data/DayOfWeekHelper.cs | 60 +++---------------- .../Entities/Libraries/Collection.cs | 1 + .../Entities/Libraries/MediaFileStream.cs | 2 + Jellyfin.Data/Entities/Permission.cs | 2 + Jellyfin.Data/Jellyfin.Data.csproj | 6 +- .../Jellyfin.Drawing.Skia.csproj | 11 ++-- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 13 ++-- .../Jellyfin.Networking.csproj | 6 +- .../Security/AuthenticationSucceededLogger.cs | 8 +-- .../Consumers/System/TaskCompletedLogger.cs | 14 ++--- .../Updates/PluginUninstalledLogger.cs | 4 +- .../Consumers/Users/UserUpdatedNotifier.cs | 6 +- .../Jellyfin.Server.Implementations.csproj | 2 + .../Users/DefaultPasswordResetProvider.cs | 2 +- .../Users/DisplayPreferencesManager.cs | 3 +- .../ApiServiceCollectionExtensions.cs | 2 +- .../CamelCaseJsonProfileFormatter.cs | 2 +- .../PascalCaseJsonProfileFormatter.cs | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 8 +-- .../Migrations/MigrationOptions.cs | 3 + .../Migrations/Routines/MigrateUserDb.cs | 2 +- Jellyfin.Server/Program.cs | 4 +- MediaBrowser.Common/IApplicationHost.cs | 4 +- MediaBrowser.Common/Json/JsonDefaults.cs | 6 +- .../MediaBrowser.Common.csproj | 6 +- MediaBrowser.Common/Net/IPHost.cs | 2 +- MediaBrowser.Common/Net/IPNetAddress.cs | 6 +- MediaBrowser.Common/Plugins/BasePlugin.cs | 2 +- MediaBrowser.Common/Plugins/BasePluginOfT.cs | 36 ++++++----- MediaBrowser.Common/Plugins/LocalPlugin.cs | 6 +- .../Progress/ActionableProgress.cs | 1 + .../Progress/SimpleProgress.cs | 1 + .../Entities/CollectionFolder.cs | 2 +- .../MediaBrowser.Controller.csproj | 6 +- .../Providers/ILocalImageProvider.cs | 2 +- .../CollectionFolderLocalImageProvider.cs | 2 +- .../Images/EpisodeLocalImageProvider.cs | 2 +- .../InternalMetadataFolderImageProvider.cs | 7 ++- .../Images/LocalImageProvider.cs | 14 ++--- .../MediaBrowser.LocalMetadata.csproj | 6 +- .../Parsers/BaseItemXmlParser.cs | 4 +- .../Parsers/BoxSetXmlParser.cs | 6 +- .../Parsers/PlaylistXmlParser.cs | 6 +- .../Savers/BaseXmlSaver.cs | 17 ++---- .../Encoder/MediaEncoder.cs | 2 +- .../Plugins/AudioDb/AlbumImageProvider.cs | 2 +- .../Plugins/AudioDb/AlbumProvider.cs | 2 +- .../Plugins/AudioDb/ArtistImageProvider.cs | 2 +- .../Plugins/AudioDb/ArtistProvider.cs | 2 +- .../Plugins/Omdb/OmdbItemProvider.cs | 2 +- .../Plugins/Omdb/OmdbProvider.cs | 2 +- .../FFprobeParserTests.cs | 2 +- .../Controllers/DashboardControllerTests.cs | 2 +- 70 files changed, 151 insertions(+), 213 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index d7b75f979..552cd501b 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -36,7 +36,7 @@ namespace Emby.Dlna private readonly ILogger _logger; private readonly IServerApplicationHost _appHost; private static readonly Assembly _assembly = typeof(DlnaManager).Assembly; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private readonly Dictionary> _profiles = new Dictionary>(StringComparer.Ordinal); diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 835dc33b0..164e6d49d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -10,8 +10,6 @@ using System.Net; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Emby.Dlna; @@ -51,7 +49,6 @@ using Jellyfin.Networking.Manager; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; -using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; @@ -470,7 +467,7 @@ namespace Emby.Server.Implementations } /// - public IReadOnlyCollection GetExports(CreationDelegate defaultFunc, bool manageLifetime = true) + public IReadOnlyCollection GetExports(CreationDelegateFactory defaultFunc, bool manageLifetime = true) { // Convert to list so this isn't executed for each iteration var parts = GetExportTypes() diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 8c5fa09f6..87ebe960a 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.Channels private readonly IProviderManager _providerManager; private readonly IMemoryCache _memoryCache; private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; /// /// Initializes a new instance of the class. diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index d78b93bd7..2ae805447 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -88,7 +88,7 @@ namespace Emby.Server.Implementations.Data _imageProcessor = imageProcessor; _typeMapper = new TypeMapper(); - _jsonOptions = JsonDefaults.GetOptions(); + _jsonOptions = JsonDefaults.Options; DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); } diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 7e0c2c1da..06acb5606 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -56,7 +56,7 @@ namespace Emby.Server.Implementations.HttpServer RemoteEndPoint = remoteEndPoint; QueryString = query; - _jsonOptions = JsonDefaults.GetOptions(); + _jsonOptions = JsonDefaults.Options; LastActivityDate = DateTime.Now; } diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 2070df31e..c2951dd15 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Library private readonly IMediaEncoder _mediaEncoder; private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IApplicationPaths appPaths) { diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index c63eb7017..b2943020c 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Library private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private IMediaSourceProvider[] _providers; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 78a82118e..44a8cdee4 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly IServerApplicationPaths _appPaths; private readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private bool _hasExited; private Stream _logFileStream; private string _targetPath; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 57424f043..c20b08088 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { private readonly string _dataPath; private readonly object _fileDataLock = new object(); - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private T[] _items; public ItemDataProvider( diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 6d7c5ac6e..1926e738f 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings private readonly ICryptoProvider _cryptoProvider; private readonly ConcurrentDictionary _tokens = new ConcurrentDictionary(); - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private DateTime _lastErrorResponse; public SchedulesDirect( diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 0760e8127..68173a0ef 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _networkManager = networkManager; _streamHelper = streamHelper; - _jsonOptions = JsonDefaults.GetOptions(); + _jsonOptions = JsonDefaults.Options; } public string Name => "HD Homerun"; diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 3f9e22106..98de848bc 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Localization private List _cultures; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; /// /// Initializes a new instance of the class. diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index c579fc8cb..700396c4c 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.Plugins _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _pluginsPath = pluginsPath; _appVersion = appVersion ?? throw new ArgumentNullException(nameof(appVersion)); - _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions()) + _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options) { WriteIndented = true }; @@ -678,7 +678,7 @@ namespace Emby.Server.Implementations.Plugins var entry = versions[x]; if (!string.Equals(lastName, entry.Name, StringComparison.OrdinalIgnoreCase)) { - entry.DllFiles.AddRange(Directory.EnumerateFiles(entry.Path, "*.dll", SearchOption.AllDirectories)); + entry.DllFiles = Directory.GetFiles(entry.Path, "*.dll", SearchOption.AllDirectories); if (entry.IsEnabledAndSupported) { lastName = entry.Name; diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index b302303f8..a145a8423 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.ScheduledTasks /// /// The options for the json Serializer. /// - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; /// /// Initializes a new instance of the class. diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 7af52ea65..fc34f93cd 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Updates _httpClientFactory = httpClientFactory; _config = config; _zipClient = zipClient; - _jsonSerializerOptions = JsonDefaults.GetOptions(); + _jsonSerializerOptions = JsonDefaults.Options; _pluginManager = pluginManager; } diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index e1c9f69f6..049a4bed7 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -25,7 +25,7 @@ namespace Jellyfin.Api.Controllers private readonly IServerConfigurationManager _configurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly JsonSerializerOptions _serializerOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _serializerOptions = JsonDefaults.Options; /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index a5aa9bfca..24285bfb9 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -45,7 +45,7 @@ namespace Jellyfin.Api.Controllers { _installationManager = installationManager; _pluginManager = pluginManager; - _serializerOptions = JsonDefaults.GetOptions(); + _serializerOptions = JsonDefaults.Options; _config = config; } diff --git a/Jellyfin.Data/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs index 4e75f4cfd..8d760a155 100644 --- a/Jellyfin.Data/DayOfWeekHelper.cs +++ b/Jellyfin.Data/DayOfWeekHelper.cs @@ -1,67 +1,21 @@ #pragma warning disable CS1591 using System; -using System.Collections.Generic; using Jellyfin.Data.Enums; namespace Jellyfin.Data { public static class DayOfWeekHelper { - public static List GetDaysOfWeek(DynamicDayOfWeek day) + public static DayOfWeek[] GetDaysOfWeek(DynamicDayOfWeek day) { - var days = new List(7); - - if (day == DynamicDayOfWeek.Sunday - || day == DynamicDayOfWeek.Weekend - || day == DynamicDayOfWeek.Everyday) + return day switch { - days.Add(DayOfWeek.Sunday); - } - - if (day == DynamicDayOfWeek.Monday - || day == DynamicDayOfWeek.Weekday - || day == DynamicDayOfWeek.Everyday) - { - days.Add(DayOfWeek.Monday); - } - - if (day == DynamicDayOfWeek.Tuesday - || day == DynamicDayOfWeek.Weekday - || day == DynamicDayOfWeek.Everyday) - { - days.Add(DayOfWeek.Tuesday); - } - - if (day == DynamicDayOfWeek.Wednesday - || day == DynamicDayOfWeek.Weekday - || day == DynamicDayOfWeek.Everyday) - { - days.Add(DayOfWeek.Wednesday); - } - - if (day == DynamicDayOfWeek.Thursday - || day == DynamicDayOfWeek.Weekday - || day == DynamicDayOfWeek.Everyday) - { - days.Add(DayOfWeek.Thursday); - } - - if (day == DynamicDayOfWeek.Friday - || day == DynamicDayOfWeek.Weekday - || day == DynamicDayOfWeek.Everyday) - { - days.Add(DayOfWeek.Friday); - } - - if (day == DynamicDayOfWeek.Saturday - || day == DynamicDayOfWeek.Weekend - || day == DynamicDayOfWeek.Everyday) - { - days.Add(DayOfWeek.Saturday); - } - - return days; + DynamicDayOfWeek.Everyday => new[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday }, + DynamicDayOfWeek.Weekday => new[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }, + DynamicDayOfWeek.Weekend => new[] { DayOfWeek.Saturday, DayOfWeek.Sunday }, + _ => new[] { (DayOfWeek)day } + }; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs index 39eded752..854f17f80 100644 --- a/Jellyfin.Data/Entities/Libraries/Collection.cs +++ b/Jellyfin.Data/Entities/Libraries/Collection.cs @@ -1,3 +1,4 @@ +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix #pragma warning disable CA2227 using System.Collections.Generic; diff --git a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs index 5b03e260e..5e27156a4 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index d92e5d9d2..0162e1acf 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix + using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 42731bb11..0340cda01 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -5,6 +5,8 @@ false true true + AllEnabledByDefault + ../jellyfin.ruleset true true true @@ -24,10 +26,6 @@ GPL-3.0-only - - ../jellyfin.ruleset - - diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 1a8415ae0..3fc44640b 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -11,6 +11,8 @@ true true enable + AllEnabledByDefault + ../jellyfin.ruleset @@ -30,6 +32,11 @@ + + + + + @@ -37,8 +44,4 @@ - - ../jellyfin.ruleset - - diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index eab5777d5..fd7cb5ec5 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -274,8 +274,8 @@ namespace Jellyfin.Drawing.Skia if (requiresTransparencyHack || forceCleanBitmap) { - using var codec = SKCodec.Create(NormalizePath(path)); - if (codec == null) + using SKCodec codec = SKCodec.Create(NormalizePath(path), out SKCodecResult res); + if (res != SKCodecResult.Success) { origin = GetSKEncodedOrigin(orientation); return null; @@ -345,11 +345,6 @@ namespace Jellyfin.Drawing.Skia private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) { - if (origin == SKEncodedOrigin.Default) - { - return bitmap; - } - var needsFlip = origin == SKEncodedOrigin.LeftBottom || origin == SKEncodedOrigin.LeftTop || origin == SKEncodedOrigin.RightBottom @@ -447,7 +442,7 @@ namespace Jellyfin.Drawing.Skia } /// - public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) + public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat) { if (inputPath.Length == 0) { @@ -459,7 +454,7 @@ namespace Jellyfin.Drawing.Skia throw new ArgumentException("String can't be empty.", nameof(outputPath)); } - var skiaOutputFormat = GetImageFormat(selectedOutputFormat); + var skiaOutputFormat = GetImageFormat(outputFormat); var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor); var hasForegroundColor = !string.IsNullOrWhiteSpace(options.ForegroundLayer); diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index f89a18426..63557e91f 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -5,6 +5,8 @@ true true enable + AllEnabledByDefault + ../jellyfin.ruleset @@ -18,10 +20,6 @@ - - ../jellyfin.ruleset - - diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs index 2f9f44ed6..8b0bd84c6 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs @@ -29,20 +29,20 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security } /// - public async Task OnEvent(GenericEventArgs e) + public async Task OnEvent(GenericEventArgs eventArgs) { await _activityManager.CreateAsync(new ActivityLog( string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("AuthenticationSucceededWithUserName"), - e.Argument.User.Name), + eventArgs.Argument.User.Name), "AuthenticationSucceeded", - e.Argument.User.Id) + eventArgs.Argument.User.Id) { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("LabelIpAddressValue"), - e.Argument.SessionInfo.RemoteEndPoint), + eventArgs.Argument.SessionInfo.RemoteEndPoint), }).ConfigureAwait(false); } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs index 05201a346..cbc9f3017 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs @@ -33,10 +33,10 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.System } /// - public async Task OnEvent(TaskCompletionEventArgs e) + public async Task OnEvent(TaskCompletionEventArgs eventArgs) { - var result = e.Result; - var task = e.Task; + var result = eventArgs.Result; + var task = eventArgs.Task; if (task.ScheduledTask is IConfigurableScheduledTask activityTask && !activityTask.IsLogged) @@ -54,14 +54,14 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.System { var vals = new List(); - if (!string.IsNullOrEmpty(e.Result.ErrorMessage)) + if (!string.IsNullOrEmpty(eventArgs.Result.ErrorMessage)) { - vals.Add(e.Result.ErrorMessage); + vals.Add(eventArgs.Result.ErrorMessage); } - if (!string.IsNullOrEmpty(e.Result.LongErrorMessage)) + if (!string.IsNullOrEmpty(eventArgs.Result.LongErrorMessage)) { - vals.Add(e.Result.LongErrorMessage); + vals.Add(eventArgs.Result.LongErrorMessage); } await _activityManager.CreateAsync(new ActivityLog( diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs index 91a30069e..eb7572ac6 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs @@ -30,13 +30,13 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Updates } /// - public async Task OnEvent(PluginUninstalledEventArgs e) + public async Task OnEvent(PluginUninstalledEventArgs eventArgs) { await _activityManager.CreateAsync(new ActivityLog( string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("PluginUninstalledWithName"), - e.Argument.Name), + eventArgs.Argument.Name), NotificationType.PluginUninstalled.ToString(), Guid.Empty)) .ConfigureAwait(false); diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs index a14911b94..9beb6f2f2 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs @@ -30,12 +30,12 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Users } /// - public async Task OnEvent(UserUpdatedEventArgs e) + public async Task OnEvent(UserUpdatedEventArgs eventArgs) { await _sessionManager.SendMessageToUserSessions( - new List { e.Argument.Id }, + new List { eventArgs.Argument.Id }, SessionMessageType.UserUpdated, - _userManager.GetUserDto(e.Argument), + _userManager.GetUserDto(eventArgs.Argument), CancellationToken.None).ConfigureAwait(false); } } diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 19c7ac567..5a5992bd6 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -6,6 +6,8 @@ true true enable + AllEnabledByDefault + ../jellyfin.ruleset diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs index 9cc1c3e5e..c99c5e4ef 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs @@ -66,7 +66,7 @@ namespace Jellyfin.Server.Implementations.Users else if (string.Equals( spr.Pin.Replace("-", string.Empty, StringComparison.Ordinal), pin.Replace("-", string.Empty, StringComparison.Ordinal), - StringComparison.InvariantCultureIgnoreCase)) + StringComparison.OrdinalIgnoreCase)) { var resetUser = userManager.GetUserByName(spr.UserName) ?? throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found"); diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index c8a589cab..a3e9516b9 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CA1307 +#pragma warning disable CA1309 using System; using System.Collections.Generic; @@ -35,7 +36,7 @@ namespace Jellyfin.Server.Implementations.Users if (prefs == null) { - prefs = new DisplayPreferences(userId, itemId, client); + prefs = new DisplayPreferences(userId, itemId, client); _dbContext.DisplayPreferences.Add(prefs); } diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 1828f1a7e..a3f49e6cb 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -225,7 +225,7 @@ namespace Jellyfin.Server.Extensions .AddJsonOptions(options => { // Update all properties that are set in JsonDefaults - var jsonOptions = JsonDefaults.GetPascalCaseOptions(); + var jsonOptions = JsonDefaults.PascalCaseOptions; // From JsonDefaults options.JsonSerializerOptions.ReadCommentHandling = jsonOptions.ReadCommentHandling; diff --git a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs index 8043989b1..c349e3dca 100644 --- a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs +++ b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Server.Formatters /// /// Initializes a new instance of the class. /// - public CamelCaseJsonProfileFormatter() : base(JsonDefaults.GetCamelCaseOptions()) + public CamelCaseJsonProfileFormatter() : base(JsonDefaults.CamelCaseOptions) { SupportedMediaTypes.Clear(); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(JsonDefaults.CamelCaseMediaType)); diff --git a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs index d0110b125..0480f5e0e 100644 --- a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs +++ b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Server.Formatters /// /// Initializes a new instance of the class. /// - public PascalCaseJsonProfileFormatter() : base(JsonDefaults.GetPascalCaseOptions()) + public PascalCaseJsonProfileFormatter() : base(JsonDefaults.PascalCaseOptions) { SupportedMediaTypes.Clear(); // Add application/json for default formatter diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 6bfb5b878..09799307b 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -13,7 +13,9 @@ true true enable - true + AllEnabledByDefault + ../jellyfin.ruleset + @@ -31,10 +33,6 @@ - - ../jellyfin.ruleset - - diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs index 816dd9ee7..c9710f1fd 100644 --- a/Jellyfin.Server/Migrations/MigrationOptions.cs +++ b/Jellyfin.Server/Migrations/MigrationOptions.cs @@ -16,9 +16,12 @@ namespace Jellyfin.Server.Migrations Applied = new List<(Guid Id, string Name)>(); } +// .Net xml serializer can't handle interfaces +#pragma warning disable CA1002 // Do not expose generic lists /// /// Gets the list of applied migration routine names. /// public List<(Guid Id, string Name)> Applied { get; } +#pragma warning restore CA1002 } } diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 33f039c39..d61c04447 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -76,7 +76,7 @@ namespace Jellyfin.Server.Migrations.Routines foreach (var entry in queryResult) { - UserMockup? mockup = JsonSerializer.Deserialize(entry[2].ToBlob(), JsonDefaults.GetOptions()); + UserMockup? mockup = JsonSerializer.Deserialize(entry[2].ToBlob(), JsonDefaults.Options); if (mockup == null) { continue; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 6ae0542c0..4f203b7a9 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -222,7 +222,7 @@ namespace Jellyfin.Server } finally { - appHost?.Dispose(); + appHost.Dispose(); } if (_restartOnShutdown) @@ -623,7 +623,7 @@ namespace Jellyfin.Server string commandLineArgsString; if (options.RestartArgs != null) { - commandLineArgsString = options.RestartArgs ?? string.Empty; + commandLineArgsString = options.RestartArgs; } else { diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index ddcf2ac17..c3e4ed6db 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Common /// /// Type to create. /// New instance of type type. - public delegate object CreationDelegate(Type type); + public delegate object CreationDelegateFactory(Type type); /// /// An interface to be implemented by the applications hosting a kernel. @@ -112,7 +112,7 @@ namespace MediaBrowser.Common /// Delegate function that gets called to create the object. /// If set to true [manage lifetime]. /// . - IReadOnlyCollection GetExports(CreationDelegate defaultFunc, bool manageLifetime = true); + IReadOnlyCollection GetExports(CreationDelegateFactory defaultFunc, bool manageLifetime = true); /// /// Gets the export types. diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 2ef24a884..177ad39fa 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Common.Json /// If the defaults must be modified the author must use the copy constructor. /// /// The default options. - public static JsonSerializerOptions GetOptions() + public static JsonSerializerOptions Options => _jsonSerializerOptions; /// @@ -72,7 +72,7 @@ namespace MediaBrowser.Common.Json /// If the defaults must be modified the author must use the copy constructor. /// /// The camelCase options. - public static JsonSerializerOptions GetCamelCaseOptions() + public static JsonSerializerOptions CamelCaseOptions => _camelCaseJsonSerializerOptions; /// @@ -83,7 +83,7 @@ namespace MediaBrowser.Common.Json /// If the defaults must be modified the author must use the copy constructor. /// /// The PascalCase options. - public static JsonSerializerOptions GetPascalCaseOptions() + public static JsonSerializerOptions PascalCaseOptions => _pascalCaseJsonSerializerOptions; } } diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 34e1934e2..0d9f78704 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -33,6 +33,8 @@ false true true + AllEnabledByDefault + ../jellyfin.ruleset true true true @@ -51,10 +53,6 @@ - - ../jellyfin.ruleset - - <_Parameter1>Jellyfin.Common.Tests diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index 4a7c70190..d67b6b8e1 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -406,7 +406,7 @@ namespace MediaBrowser.Common.Net } // If we haven't resolved before, or our timer has run out... - if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved?.AddMinutes(Timeout))) + if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout))) { _lastResolved = DateTime.UtcNow; ResolveHostInternal().GetAwaiter().GetResult(); diff --git a/MediaBrowser.Common/Net/IPNetAddress.cs b/MediaBrowser.Common/Net/IPNetAddress.cs index 5fab52eac..59e37a5c6 100644 --- a/MediaBrowser.Common/Net/IPNetAddress.cs +++ b/MediaBrowser.Common/Net/IPNetAddress.cs @@ -216,11 +216,11 @@ namespace MediaBrowser.Common.Net } /// - public override bool Equals(IPAddress address) + public override bool Equals(IPAddress ip) { - if (address != null && !address.Equals(IPAddress.None) && !Address.Equals(IPAddress.None)) + if (ip != null && !ip.Equals(IPAddress.None) && !Address.Equals(IPAddress.None)) { - return address.Equals(Address); + return ip.Equals(Address); } return false; diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 7b162c0e1..ad5a7338d 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Common.Plugins /// Gets a value indicating whether the plugin can be uninstalled. /// public bool CanUninstall => !Path.GetDirectoryName(AssemblyFilePath) - .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.InvariantCulture); + .Equals(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), StringComparison.Ordinal); /// /// Gets the plugin info. diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs index d5c780851..99c226f50 100644 --- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs +++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs @@ -39,29 +39,27 @@ namespace MediaBrowser.Common.Plugins { ApplicationPaths = applicationPaths; XmlSerializer = xmlSerializer; - if (this is IPluginAssembly assemblyPlugin) + + var assembly = GetType().Assembly; + var assemblyName = assembly.GetName(); + var assemblyFilePath = assembly.Location; + + var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); + if (!Directory.Exists(dataFolderPath) && Version != null) { - var assembly = GetType().Assembly; - var assemblyName = assembly.GetName(); - var assemblyFilePath = assembly.Location; + // Try again with the version number appended to the folder name. + dataFolderPath = dataFolderPath + "_" + Version.ToString(); + } - var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); - if (!Directory.Exists(dataFolderPath) && Version != null) - { - // Try again with the version number appended to the folder name. - dataFolderPath = dataFolderPath + "_" + Version.ToString(); - } + SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); - assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); + var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true); + if (idAttributes.Length > 0) + { + var attribute = (GuidAttribute)idAttributes[0]; + var assemblyId = new Guid(attribute.Value); - var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true); - if (idAttributes.Length > 0) - { - var attribute = (GuidAttribute)idAttributes[0]; - var assemblyId = new Guid(attribute.Value); - - assemblyPlugin.SetId(assemblyId); - } + SetId(assemblyId); } } diff --git a/MediaBrowser.Common/Plugins/LocalPlugin.cs b/MediaBrowser.Common/Plugins/LocalPlugin.cs index 23b6cfa81..12a1ad1ec 100644 --- a/MediaBrowser.Common/Plugins/LocalPlugin.cs +++ b/MediaBrowser.Common/Plugins/LocalPlugin.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Common.Plugins public LocalPlugin(string path, bool isSupported, PluginManifest manifest) { Path = path; - DllFiles = new List(); + DllFiles = Array.Empty(); _supported = isSupported; Manifest = manifest; } @@ -59,9 +59,9 @@ namespace MediaBrowser.Common.Plugins public string Path { get; } /// - /// Gets the list of dll files for this plugin. + /// Gets or sets the list of dll files for this plugin. /// - public List DllFiles { get; } + public IReadOnlyList DllFiles { get; set; } /// /// Gets or sets the instance of this plugin. diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs index d5bcd5be9..fe7cb1078 100644 --- a/MediaBrowser.Common/Progress/ActionableProgress.cs +++ b/MediaBrowser.Common/Progress/ActionableProgress.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable CA1003 using System; diff --git a/MediaBrowser.Common/Progress/SimpleProgress.cs b/MediaBrowser.Common/Progress/SimpleProgress.cs index d75675bf1..988d8ad34 100644 --- a/MediaBrowser.Common/Progress/SimpleProgress.cs +++ b/MediaBrowser.Common/Progress/SimpleProgress.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable CA1003 using System; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 65fd1654c..76b6d39a9 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Entities /// public class CollectionFolder : Folder, ICollectionFolder { - private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; public static IXmlSerializer XmlSerializer { get; set; } public static IServerApplicationHost ApplicationHost { get; set; } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index d487a324f..8c68b47dd 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -34,6 +34,8 @@ false true true + AllEnabledByDefault + ../jellyfin.ruleset true true true @@ -52,8 +54,4 @@ - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs index c129eddb3..f78bd6ddf 100644 --- a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs +++ b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs @@ -10,6 +10,6 @@ namespace MediaBrowser.Controller.Providers /// public interface ILocalImageProvider : IImageProvider { - List GetImages(BaseItem item, IDirectoryService directoryService); + IEnumerable GetImages(BaseItem item, IDirectoryService directoryService); } } diff --git a/MediaBrowser.LocalMetadata/Images/CollectionFolderLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderLocalImageProvider.cs index 556bb6a0e..b6189bcdd 100644 --- a/MediaBrowser.LocalMetadata/Images/CollectionFolderLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/CollectionFolderLocalImageProvider.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.LocalMetadata.Images } /// - public List GetImages(BaseItem item, IDirectoryService directoryService) + public IEnumerable GetImages(BaseItem item, IDirectoryService directoryService) { var collectionFolder = (CollectionFolder)item; diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index 393ad2efb..2d3b2d889 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.LocalMetadata.Images } /// - public List GetImages(BaseItem item, IDirectoryService directoryService) + public IEnumerable GetImages(BaseItem item, IDirectoryService directoryService) { var parentPath = Path.GetDirectoryName(item.Path); diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs index 509b5d700..10d691b3e 100644 --- a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -69,13 +70,13 @@ namespace MediaBrowser.LocalMetadata.Images } /// - public List GetImages(BaseItem item, IDirectoryService directoryService) + public IEnumerable GetImages(BaseItem item, IDirectoryService directoryService) { var path = item.GetInternalMetadataPath(); if (!Directory.Exists(path)) { - return new List(); + return Enumerable.Empty(); } try @@ -85,7 +86,7 @@ namespace MediaBrowser.LocalMetadata.Images catch (IOException ex) { _logger.LogError(ex, "Error while getting images for {Library}", item.Name); - return new List(); + return Enumerable.Empty(); } } } diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs index 84c3ed8b0..7ad8c24e8 100644 --- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs @@ -108,7 +108,7 @@ namespace MediaBrowser.LocalMetadata.Images { if (!item.IsFileProtocol) { - return new List(); + return Enumerable.Empty(); } var path = item.ContainingFolderPath; @@ -116,7 +116,7 @@ namespace MediaBrowser.LocalMetadata.Images // Exit if the cache dir does not exist, alternative solution is to create it, but that's a lot of empty dirs... if (!Directory.Exists(path)) { - return Array.Empty(); + return Enumerable.Empty(); } if (includeDirectories) @@ -133,7 +133,7 @@ namespace MediaBrowser.LocalMetadata.Images } /// - public List GetImages(BaseItem item, IDirectoryService directoryService) + public IEnumerable GetImages(BaseItem item, IDirectoryService directoryService) { var files = GetFiles(item, true, directoryService).ToList(); @@ -151,7 +151,7 @@ namespace MediaBrowser.LocalMetadata.Images /// The images path. /// Instance of the interface. /// The local image info. - public List GetImages(BaseItem item, string path, IDirectoryService directoryService) + public IEnumerable GetImages(BaseItem item, string path, IDirectoryService directoryService) { return GetImages(item, new[] { path }, directoryService); } @@ -163,7 +163,7 @@ namespace MediaBrowser.LocalMetadata.Images /// The image paths. /// Instance of the interface. /// The local image info. - public List GetImages(BaseItem item, IEnumerable paths, IDirectoryService directoryService) + public IEnumerable GetImages(BaseItem item, IEnumerable paths, IDirectoryService directoryService) { IEnumerable files = paths.SelectMany(i => _fileSystem.GetFiles(i, BaseItem.SupportedImageExtensions, true, false)); @@ -181,9 +181,7 @@ namespace MediaBrowser.LocalMetadata.Images { if (supportParentSeriesFiles) { - var season = item as Season; - - if (season != null) + if (item is Season season) { PopulateSeasonImagesFromSeriesFolder(season, images, directoryService); } diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 1792f1d9b..eb2077a5f 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -16,6 +16,8 @@ true true enable + AllEnabledByDefault + ../jellyfin.ruleset @@ -29,8 +31,4 @@ - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index b0afb834b..5f620634f 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -1275,8 +1275,8 @@ namespace MediaBrowser.LocalMetadata.Parsers // Only split by comma if there is no pipe in the string // We have to be careful to not split names like Matthew, Jr. - var separator = value.IndexOf('|', StringComparison.Ordinal) == -1 - && value.IndexOf(';', StringComparison.Ordinal) == -1 ? new[] { ',' } : new[] { '|', ';' }; + var separator = !value.Contains('|', StringComparison.Ordinal) + && !value.Contains(';', StringComparison.Ordinal) ? new[] { ',' } : new[] { '|', ';' }; value = value.Trim().Trim(separator); diff --git a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs index ff846830b..7df800971 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.LocalMetadata.Parsers } /// - protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult item) + protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult itemResult) { switch (reader.Name) { @@ -33,7 +33,7 @@ namespace MediaBrowser.LocalMetadata.Parsers { using (var subReader = reader.ReadSubtree()) { - FetchFromCollectionItemsNode(subReader, item); + FetchFromCollectionItemsNode(subReader, itemResult); } } else @@ -44,7 +44,7 @@ namespace MediaBrowser.LocalMetadata.Parsers break; default: - base.FetchDataFromXmlNode(reader, item); + base.FetchDataFromXmlNode(reader, itemResult); break; } } diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs index 78c0fa8ad..b84307cb2 100644 --- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs @@ -23,9 +23,9 @@ namespace MediaBrowser.LocalMetadata.Parsers } /// - protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult result) + protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult itemResult) { - var item = result.Item; + var item = itemResult.Item; switch (reader.Name) { @@ -53,7 +53,7 @@ namespace MediaBrowser.LocalMetadata.Parsers break; default: - base.FetchDataFromXmlNode(reader, result); + base.FetchDataFromXmlNode(reader, itemResult); break; } } diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index e59fcb965..dfbce5f49 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Text; using System.Threading; using System.Xml; -using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -37,7 +36,7 @@ namespace MediaBrowser.LocalMetadata.Savers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - public BaseXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) + protected BaseXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) { FileSystem = fileSystem; ConfigurationManager = configurationManager; @@ -421,20 +420,17 @@ namespace MediaBrowser.LocalMetadata.Savers writer.WriteEndElement(); } - var boxset = item as BoxSet; - if (boxset != null) + if (item is BoxSet boxset) { AddLinkedChildren(boxset, writer, "CollectionItems", "CollectionItem"); } - var playlist = item as Playlist; - if (playlist != null && !Playlist.IsPlaylistFile(playlist.Path)) + if (item is Playlist playlist && !Playlist.IsPlaylistFile(playlist.Path)) { AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem"); } - var hasShares = item as IHasShares; - if (hasShares != null) + if (item is IHasShares hasShares) { AddShares(hasShares, writer); } @@ -542,10 +538,5 @@ namespace MediaBrowser.LocalMetadata.Savers writer.WriteEndElement(); } - - private bool IsPersonType(PersonInfo person, string type) - { - return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); - } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 8a25a64c7..47cf020b4 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -86,7 +86,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _localization = localization; _encodingHelperFactory = encodingHelperFactory; _startupOptionFFmpegPath = config.GetValue(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty; - _jsonSerializerOptions = JsonDefaults.GetOptions(); + _jsonSerializerOptions = JsonDefaults.Options; } /// diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index cd9e47743..2adb11908 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 0a79f5bb5..00feeec1f 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; public static AudioDbAlbumProvider Current; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index 36700d191..b8095ff04 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; public AudioDbArtistImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory) { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index 4b1d91567..59ecbc017 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 97fcbfb6f..428b0ded1 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb _configurationManager = configurationManager; _appHost = appHost; - _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions()); + _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options); _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter()); _jsonOptions.Converters.Add(new JsonOmdbNotAvailableInt32Converter()); } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index e3301ff32..d35805a84 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb _configurationManager = configurationManager; _appHost = appHost; - _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions()); + _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options); _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter()); _jsonOptions.Converters.Add(new JsonOmdbNotAvailableInt32Converter()); } diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs index c39ef0ce9..415682e85 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs @@ -16,7 +16,7 @@ namespace Jellyfin.MediaEncoding.Tests var path = Path.Join("Test Data", fileName); using (var stream = File.OpenRead(path)) { - await JsonSerializer.DeserializeAsync(stream, JsonDefaults.GetOptions()).ConfigureAwait(false); + await JsonSerializer.DeserializeAsync(stream, JsonDefaults.Options).ConfigureAwait(false); } } } diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs index 86d6326d8..f5411dcb8 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers public sealed class DashboardControllerTests : IClassFixture { private readonly JellyfinApplicationFactory _factory; - private readonly JsonSerializerOptions _jsonOpions = JsonDefaults.GetOptions(); + private readonly JsonSerializerOptions _jsonOpions = JsonDefaults.Options; public DashboardControllerTests(JellyfinApplicationFactory factory) { From 46a41ecba66f8055a9e7de4d0dd4a1e07ea371a3 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 9 Mar 2021 17:01:14 +0100 Subject: [PATCH 131/402] Sunday isn't a weekend --- Jellyfin.Data/DayOfWeekHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Data/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs index 8d760a155..1bfd4a0c6 100644 --- a/Jellyfin.Data/DayOfWeekHelper.cs +++ b/Jellyfin.Data/DayOfWeekHelper.cs @@ -12,8 +12,8 @@ namespace Jellyfin.Data return day switch { DynamicDayOfWeek.Everyday => new[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday }, - DynamicDayOfWeek.Weekday => new[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }, - DynamicDayOfWeek.Weekend => new[] { DayOfWeek.Saturday, DayOfWeek.Sunday }, + DynamicDayOfWeek.Weekday => new[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }, + DynamicDayOfWeek.Weekend => new[] { DayOfWeek.Sunday, DayOfWeek.Saturday }, _ => new[] { (DayOfWeek)day } }; } From 01dfa8801a74f8c6802aea33d9bce0e421601faa Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Mar 2021 22:40:23 +0100 Subject: [PATCH 132/402] Fix GetDaysOfWeek behavior --- Jellyfin.Data/DayOfWeekHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Data/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs index 1bfd4a0c6..b7ba30180 100644 --- a/Jellyfin.Data/DayOfWeekHelper.cs +++ b/Jellyfin.Data/DayOfWeekHelper.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Data { return day switch { - DynamicDayOfWeek.Everyday => new[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday }, + DynamicDayOfWeek.Everyday => new[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday }, DynamicDayOfWeek.Weekday => new[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }, DynamicDayOfWeek.Weekend => new[] { DayOfWeek.Sunday, DayOfWeek.Saturday }, _ => new[] { (DayOfWeek)day } From b1f0c5eb4935904bea3a784e147d403b9d43097d Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 13 Mar 2021 22:16:12 +0000 Subject: [PATCH 133/402] Update NetworkExtensions.cs changed description --- MediaBrowser.Common/Net/NetworkExtensions.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Common/Net/NetworkExtensions.cs b/MediaBrowser.Common/Net/NetworkExtensions.cs index cd0c2ea24..26f21b5f1 100644 --- a/MediaBrowser.Common/Net/NetworkExtensions.cs +++ b/MediaBrowser.Common/Net/NetworkExtensions.cs @@ -27,10 +27,11 @@ namespace MediaBrowser.Common.Net /// /// The . /// Item to add. - /// True if subnets that overlap should be merged (default). - public static void AddItem(this Collection source, IPObject item, bool unique = true) + /// If true the values are treated as subnets. + /// If false items are addresses. + public static void AddItem(this Collection source, IPObject item, bool itemsAreNetworks = true) { - if (!source.ContainsAddress(item) || !unique) + if (!source.ContainsAddress(item) || !itemsAreNetworks) { source.Add(item); } From 72d3eed15c0a24612cc8ff90475a9e19ff8c7558 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 14 Mar 2021 01:07:53 +0100 Subject: [PATCH 134/402] Fix integration test project --- Jellyfin.sln | 9 +++++++-- .../Jellyfin.Server.Integration.Tests.csproj | 1 - tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 7 ++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Jellyfin.sln b/Jellyfin.sln index 7b81f4346..0f36e283c 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -76,10 +76,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -214,6 +214,10 @@ Global {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.Build.0 = Release|Any CPU + {68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -230,6 +234,7 @@ Global {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {68B0B823-A5AC-4E8B-82EA-965AAC7BF76E} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 49004966b..b0a38736a 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -23,7 +23,6 @@ - diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 65ea28e94..a310b0ea9 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -5,6 +5,8 @@ false true enable + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -22,7 +24,6 @@ - @@ -32,8 +33,4 @@ - - ../jellyfin-tests.ruleset - - From 6087831aa65398a6305570c4dc84f0bc2613b842 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 14 Mar 2021 17:30:25 +0000 Subject: [PATCH 135/402] Fixed selection of correct interface ip --- Jellyfin.Networking/Manager/NetworkManager.cs | 26 +++++++++++++------ MediaBrowser.Common/Net/NetworkExtensions.cs | 7 ++--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index bf121737b..9ce3b7e49 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -274,7 +274,7 @@ namespace Jellyfin.Networking.Manager if (_bindExclusions.Count > 0) { // Return all the interfaces except the ones specifically excluded. - return _interfaceAddresses.Exclude(_bindExclusions); + return _interfaceAddresses.Exclude(_bindExclusions, false); } if (individualInterfaces) @@ -299,7 +299,7 @@ namespace Jellyfin.Networking.Manager } // Remove any excluded bind interfaces. - return _bindAddresses.Exclude(_bindExclusions); + return _bindAddresses.Exclude(_bindExclusions, false); } /// @@ -388,7 +388,7 @@ namespace Jellyfin.Networking.Manager // Get the first LAN interface address that isn't a loopback. var interfaces = CreateCollection( _interfaceAddresses - .Exclude(_bindExclusions) + .Exclude(_bindExclusions, false) .Where(IsInLocalNetwork) .OrderBy(p => p.Tag)); @@ -396,6 +396,16 @@ namespace Jellyfin.Networking.Manager { if (haveSource) { + foreach (var intf in interfaces) + { + if (intf.Address.Equals(source.Address)) + { + result = FormatIP6String(intf.Address); + _logger.LogDebug("{Source}: GetBindInterface: Has found matching interface. {Result}", source, result); + return result; + } + } + // Does the request originate in one of the interface subnets? // (For systems with multiple internal network cards, and multiple subnets) foreach (var intf in interfaces) @@ -522,10 +532,10 @@ namespace Jellyfin.Networking.Manager { if (filter == null) { - return _lanSubnets.Exclude(_excludedSubnets).AsNetworks(); + return _lanSubnets.Exclude(_excludedSubnets, true).AsNetworks(); } - return _lanSubnets.Exclude(filter); + return _lanSubnets.Exclude(filter, true); } /// @@ -1018,7 +1028,7 @@ namespace Jellyfin.Networking.Manager _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString()); _logger.LogInformation("Defined LAN exclusions : {0}", _excludedSubnets.AsString()); - _logger.LogInformation("Using LAN addresses: {0}", _lanSubnets.Exclude(_excludedSubnets).AsNetworks().AsString()); + _logger.LogInformation("Using LAN addresses: {0}", _lanSubnets.Exclude(_excludedSubnets, true).AsNetworks().AsString()); } } @@ -1207,7 +1217,7 @@ namespace Jellyfin.Networking.Manager private bool MatchesBindInterface(IPObject source, bool isInExternalSubnet, out string result) { result = string.Empty; - var addresses = _bindAddresses.Exclude(_bindExclusions); + var addresses = _bindAddresses.Exclude(_bindExclusions, false); int count = addresses.Count; if (count == 1 && (_bindAddresses[0].Equals(IPAddress.Any) || _bindAddresses[0].Equals(IPAddress.IPv6Any))) @@ -1292,7 +1302,7 @@ namespace Jellyfin.Networking.Manager result = string.Empty; // Get the first WAN interface address that isn't a loopback. var extResult = _interfaceAddresses - .Exclude(_bindExclusions) + .Exclude(_bindExclusions, false) .Where(p => !IsInLocalNetwork(p)) .OrderBy(p => p.Tag); diff --git a/MediaBrowser.Common/Net/NetworkExtensions.cs b/MediaBrowser.Common/Net/NetworkExtensions.cs index 26f21b5f1..93cfb4817 100644 --- a/MediaBrowser.Common/Net/NetworkExtensions.cs +++ b/MediaBrowser.Common/Net/NetworkExtensions.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Common.Net /// /// The . /// Item to add. - /// If true the values are treated as subnets. + /// If true the values are treated as subnets. /// If false items are addresses. public static void AddItem(this Collection source, IPObject item, bool itemsAreNetworks = true) { @@ -192,8 +192,9 @@ namespace MediaBrowser.Common.Net /// /// The . /// Items to exclude. + /// Collection is a network collection. /// A new collection, with the items excluded. - public static Collection Exclude(this Collection source, Collection excludeList) + public static Collection Exclude(this Collection source, Collection excludeList, bool isNetwork) { if (source.Count == 0 || excludeList == null) { @@ -218,7 +219,7 @@ namespace MediaBrowser.Common.Net if (!found) { - results.AddItem(outer); + results.AddItem(outer, isNetwork); } } From ab0cff8556403e123642dc9717ba778329554634 Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 14 Mar 2021 19:56:45 +0100 Subject: [PATCH 136/402] do not resolve episode-like files if they are in extras folders --- .../Library/Resolvers/BaseVideoResolver.cs | 2 +- .../Library/Resolvers/Books/BookResolver.cs | 2 +- .../Library/Resolvers/Movies/MovieResolver.cs | 208 +++++++++--------- .../Library/Resolvers/TV/EpisodeResolver.cs | 12 +- .../Resolvers/BaseItemResolver.cs | 2 +- 5 files changed, 114 insertions(+), 112 deletions(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index 2f5e46038..dd92e252f 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers /// /// The args. /// `0. - protected override T Resolve(ItemResolveArgs args) + public override T Resolve(ItemResolveArgs args) { return ResolveVideo(args, false); } diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 86242d137..0525c7e30 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books { private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" }; - protected override Book Resolve(ItemResolveArgs args) + public override Book Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 8ef7172de..714bc3a84 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -69,6 +69,110 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return result; } + /// + /// Resolves the specified args. + /// + /// The args. + /// Video. + public override Video Resolve(ItemResolveArgs args) + { + var collectionType = args.GetCollectionType(); + + // Find movies with their own folders + if (args.IsDirectory) + { + if (IsInvalid(args.Parent, collectionType)) + { + return null; + } + + var files = args.FileSystemChildren + .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) + .ToList(); + + if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) + { + return FindMovie(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); + } + + if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) + { + return FindMovie /// The args. /// Episode. - protected override Episode Resolve(ItemResolveArgs args) + public override Episode Resolve(ItemResolveArgs args) { var parent = args.Parent; @@ -34,11 +35,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV season = parent.GetParents().OfType().FirstOrDefault(); } - // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something + // If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something // Also handle flat tv folders - if (season != null || - string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || - args.HasParent()) + if ((season != null || + string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || + args.HasParent()) + && !BaseItem.AllExtrasTypesFolderNames.Contains(parent.Name, StringComparer.OrdinalIgnoreCase)) { var episode = ResolveVideo(args, false); diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs index 67acdd9a3..25128a5cd 100644 --- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// `0. - protected virtual T Resolve(ItemResolveArgs args) + public virtual T Resolve(ItemResolveArgs args) { return null; } From 025e351f619137426a0a64074ca2ada863ccc493 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 15 Mar 2021 08:25:20 +0100 Subject: [PATCH 137/402] add unit tests --- .../Library/Resolvers/BaseVideoResolver.cs | 2 +- .../Library/Resolvers/TV/EpisodeResolver.cs | 20 +++--- .../Library/EpisodeResolverTest.cs | 65 +++++++++++++++++++ 3 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index dd92e252f..6e688693b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Library.Resolvers /// The args. /// if set to true [parse name]. /// ``0. - protected TVideoType ResolveVideo(ItemResolveArgs args, bool parseName) + protected virtual TVideoType ResolveVideo(ItemResolveArgs args, bool parseName) where TVideoType : Video, new() { var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index da65c746d..9b4cd7a3d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -12,6 +12,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// public class EpisodeResolver : BaseVideoResolver { + /// + /// Initializes a new instance of the class. + /// + /// The library manager. + public EpisodeResolver(ILibraryManager libraryManager) + : base(libraryManager) + { + } + /// /// Resolves the specified args. /// @@ -40,7 +49,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV if ((season != null || string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || args.HasParent()) - && !BaseItem.AllExtrasTypesFolderNames.Contains(parent.Name, StringComparer.OrdinalIgnoreCase)) + && (parent is Series || !BaseItem.AllExtrasTypesFolderNames.Contains(parent.Name, StringComparer.OrdinalIgnoreCase))) { var episode = ResolveVideo(args, false); @@ -76,14 +85,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - - /// - /// Initializes a new instance of the class. - /// - /// The library manager. - public EpisodeResolver(ILibraryManager libraryManager) - : base(libraryManager) - { - } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs new file mode 100644 index 000000000..876519215 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs @@ -0,0 +1,65 @@ +using System; +using Emby.Server.Implementations.Library.Resolvers.TV; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using Moq; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Library +{ + public class EpisodeResolverTest + { + [Fact] + public void Resolve_GivenVideoInExtrasFolder_DoesNotResolveToEpisode() + { + var season = new Season { Name = "Season 1" }; + var parent = new Folder { Name = "extras" }; + var libraryManagerMock = new Mock(); + libraryManagerMock.Setup(x => x.GetItemById(It.IsAny())).Returns(season); + + var episodeResolver = new EpisodeResolver(libraryManagerMock.Object); + var itemResolveArgs = new ItemResolveArgs( + Mock.Of(), + Mock.Of()) + { + Parent = parent, + CollectionType = CollectionType.TvShows, + Path = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv" + }; + + Assert.Null(episodeResolver.Resolve(itemResolveArgs)); + } + + [Fact] + public void Resolve_GivenVideoInExtrasSeriesFolder_ResolvesToEpisode() + { + var series = new Series { Name = "Extras" }; + + // Have to create a mock because of moq proxies not being castable to a concrete implementation + // https://github.com/jellyfin/jellyfin/blob/ab0cff8556403e123642dc9717ba778329554634/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs#L48 + var episodeResolver = new EpisodeResolverMock(Mock.Of()); + var itemResolveArgs = new ItemResolveArgs( + Mock.Of(), + Mock.Of()) + { + Parent = series, + CollectionType = CollectionType.TvShows, + Path = "Extras/Extras S01E01.mkv" + }; + Assert.NotNull(episodeResolver.Resolve(itemResolveArgs)); + } + + private class EpisodeResolverMock : EpisodeResolver + { + public EpisodeResolverMock(ILibraryManager libraryManager) : base(libraryManager) + { + } + + protected override TVideoType ResolveVideo(ItemResolveArgs args, bool parseName) => new (); + } + } +} From 23c3188501447db9def5cd5345112a76cfe04b83 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 15 Mar 2021 23:24:59 +0100 Subject: [PATCH 138/402] revert underscore as a multiversion separator --- Emby.Naming/Video/VideoListResolver.cs | 1 - tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index d71d60954..7b6a1705b 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -235,7 +235,6 @@ namespace Emby.Naming.Video // The CleanStringParser should have removed common keywords etc. return string.IsNullOrEmpty(testFilename) || testFilename[0] == '-' - || testFilename[0] == '_' || Regex.IsMatch(testFilename, @"^\[([^]]*)\]"); } diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index 2af666759..6e803593e 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -295,12 +295,9 @@ namespace Jellyfin.Naming.Tests.Video FullName = i }).ToList()).ToList(); - Assert.Single(result); + Assert.Equal(7, result.Count); Assert.Empty(result[0].Extras); - Assert.Equal(6, result[0].AlternateVersions.Count); - Assert.False(result[0].AlternateVersions[2].Is3D); - Assert.True(result[0].AlternateVersions[3].Is3D); - Assert.True(result[0].AlternateVersions[4].Is3D); + Assert.Empty(result[0].AlternateVersions); } [Fact] From e14311ca8b86fa14033d2c02f9b003f235fa7e6a Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 16 Mar 2021 17:40:56 +0000 Subject: [PATCH 139/402] Translated using Weblate (Esperanto) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/eo/ --- .../Localization/Core/eo.json | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/eo.json b/Emby.Server.Implementations/Localization/Core/eo.json index 3ff7eddae..ca615cc8c 100644 --- a/Emby.Server.Implementations/Localization/Core/eo.json +++ b/Emby.Server.Implementations/Localization/Core/eo.json @@ -22,5 +22,26 @@ "Artists": "Artistoj", "Application": "Aplikaĵo", "AppDeviceValues": "Aplikaĵo: {0}, Aparato: {1}", - "Albums": "Albumoj" + "Albums": "Albumoj", + "TasksLibraryCategory": "Libraro", + "VersionNumber": "Versio {0}", + "UserDownloadingItemWithValues": "{0} elŝutas {1}", + "UserCreatedWithName": "Uzanto {0} kreiĝis", + "User": "Uzanto", + "System": "Sistemo", + "Songs": "Kantoj", + "ScheduledTaskStartedWithName": "{0} komencis", + "ScheduledTaskFailedWithName": "{0} malsukcesis", + "PluginUninstalledWithName": "{0} malinstaliĝis", + "PluginInstalledWithName": "{0} instaliĝis", + "Plugin": "Kromprogramo", + "Playlists": "Ludlistoj", + "Photos": "Fotoj", + "NotificationOptionPluginUninstalled": "Kromprogramo malinstaliĝis", + "NotificationOptionNewLibraryContent": "Nova enhavo aldoniĝis", + "NotificationOptionPluginInstalled": "Kromprogramo instaliĝis", + "MusicVideos": "Muzikvideoj", + "LabelIpAddressValue": "IP-adreso: {0}", + "Genres": "Ĝenroj", + "DeviceOfflineWithName": "{0} malkonektis" } From 14cbd22fbe0a1989f70895edbd0a0f52d850d55e Mon Sep 17 00:00:00 2001 From: David Date: Tue, 16 Mar 2021 21:11:34 +0100 Subject: [PATCH 140/402] Use Helper Methods for provider url parsing --- .../Library/PathExtensions.cs | 5 +- .../Providers/ProviderIdParsers.cs | 119 ++++++++++++++++++ .../Parsers/BaseNfoParser.cs | 35 +++--- .../Parsers/SeriesNfoParser.cs | 3 - .../Providers/ProviderIdParserTests.cs | 63 ++++++++++ 5 files changed, 201 insertions(+), 24 deletions(-) create mode 100644 MediaBrowser.Common/Providers/ProviderIdParsers.cs create mode 100644 tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 7dcc925c2..72fe5a854 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.RegularExpressions; +using MediaBrowser.Common.Providers; namespace Emby.Server.Implementations.Library { @@ -43,8 +44,8 @@ namespace Emby.Server.Implementations.Library // for imdbid we also accept pattern matching if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase)) { - var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase); - return m.Success ? m.Value : null; + var match = ProviderIdParsers.TryParseImdbId(str, out var imdbId); + return match ? imdbId : null; } return null; diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs new file mode 100644 index 000000000..56e0112dd --- /dev/null +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -0,0 +1,119 @@ +#nullable enable + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace MediaBrowser.Common.Providers +{ + /// + /// Parsers for provider ids. + /// + public static class ProviderIdParsers + { + /// + /// Parses an IMDb id from a string. + /// + /// The text to parse. + /// The parsed IMDb id. + /// True if parsing was successful, false otherwise. + public static bool TryParseImdbId(string text, [NotNullWhen(true)] out string? imdbId) + { + var span = text.AsSpan(); + var tt = "tt".AsSpan(); + + while (true) + { + var ttPos = span.IndexOf(tt); + if (ttPos == -1) + { + imdbId = default; + return false; + } + + span = span.Slice(ttPos + tt.Length); + + int i = 0; + // IMDb id has a maximum of 8 digits + int max = span.Length > 8 ? 8 : span.Length; + for (; i < max; i++) + { + var c = span[i]; + + if (c < '0' || c > '9') + { + break; + } + } + + // IMDb id has a minimum of 7 digits + if (i >= 7) + { + imdbId = string.Concat(tt, span.Slice(0, i)); + return true; + } + } + } + + /// + /// Parses an TMDb id from a movie url. + /// + /// The text with the url to parse. + /// The parsed TMDb id. + /// True if parsing was successful, false otherwise. + public static bool TryParseTmdbMovieId(string text, [NotNullWhen(true)] out string? tmdbId) + => TryParseProviderId(text, "themoviedb.org/movie/", out tmdbId); + + /// + /// Parses an TMDb id from a series url. + /// + /// The text with the url to parse. + /// The parsed TMDb id. + /// True if parsing was successful, false otherwise. + public static bool TryParseTmdbSeriesId(string text, [NotNullWhen(true)] out string? tmdbId) + => TryParseProviderId(text, "themoviedb.org/tv/", out tmdbId); + + /// + /// Parses an TVDb id from a url. + /// + /// The text with the url to parse. + /// The parsed TVDb id. + /// True if parsing was successful, false otherwise. + public static bool TryParseTvdbId(string text, [NotNullWhen(true)] out string? tvdbId) + => TryParseProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId); + + private static bool TryParseProviderId(string text, string searchString, [NotNullWhen(true)] out string? providerId) + { + var span = text.AsSpan(); + var searchSpan = searchString.AsSpan(); + + while (true) + { + var searchPos = span.IndexOf(searchSpan); + if (searchPos == -1) + { + providerId = default; + return false; + } + + span = span.Slice(searchPos + searchSpan.Length); + + int i = 0; + for (; i < span.Length; i++) + { + var c = span[i]; + + if (c < '0' || c > '9') + { + break; + } + } + + if (i >= 1) + { + providerId = span.Slice(0, i).ToString(); + return true; + } + } + } + } +} diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 3129c131d..d2fa120e8 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -6,11 +6,12 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text; -using System.Text.RegularExpressions; using System.Threading; using System.Xml; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Providers; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -63,8 +64,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected virtual bool SupportsUrlAfterClosingXmlTag => false; - protected virtual string TmdbRegex => "themoviedb\\.org\\/movie\\/([0-9]+)"; - /// /// Fetches metadata for an item from one xml file. /// @@ -220,31 +219,29 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected void ParseProviderLinks(T item, string xml) { - // IMDB: - // https://www.imdb.com/title/tt4154796 - var imdbRegex = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.Compiled); - if (imdbRegex.Success) + if (ProviderIdParsers.TryParseImdbId(xml, out var imdbId)) { - item.SetProviderId(MetadataProvider.Imdb, imdbRegex.Value); + item.SetProviderId(MetadataProvider.Imdb, imdbId); } - // TMDB: - // https://www.themoviedb.org/movie/30287-fallo (movie) - // https://www.themoviedb.org/tv/1668-friends (tv) - var tmdbRegex = Regex.Match(xml, TmdbRegex, RegexOptions.Compiled); - if (tmdbRegex.Success) + if (item is Movie) { - item.SetProviderId(MetadataProvider.Tmdb, tmdbRegex.Groups[1].Value); + if (ProviderIdParsers.TryParseTmdbMovieId(xml, out var tmdbId)) + { + item.SetProviderId(MetadataProvider.Tmdb, tmdbId); + } } - // TVDB: - // https://www.thetvdb.com/?tab=series&id=121361 if (item is Series) { - var tvdbRegex = Regex.Match(xml, "thetvdb\\.com\\/\\?tab=series\\&id=([0-9]+)", RegexOptions.Compiled); - if (tvdbRegex.Success) + if (ProviderIdParsers.TryParseTmdbSeriesId(xml, out var tmdbId)) { - item.SetProviderId(MetadataProvider.Tvdb, tvdbRegex.Groups[1].Value); + item.SetProviderId(MetadataProvider.Tmdb, tmdbId); + } + + if (ProviderIdParsers.TryParseTvdbId(xml, out var tvdbId)) + { + item.SetProviderId(MetadataProvider.Tvdb, tvdbId); } } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index c09781b1a..1dce378dc 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -35,9 +35,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// protected override bool SupportsUrlAfterClosingXmlTag => true; - /// - protected override string TmdbRegex => "themoviedb\\.org\\/tv\\/([0-9]+)"; - /// protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult itemResult) { diff --git a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs new file mode 100644 index 000000000..a493dda64 --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs @@ -0,0 +1,63 @@ +using MediaBrowser.Common.Providers; +using Xunit; + +namespace Jellyfin.Common.Tests.Providers +{ + public class ProviderIdParserTests + { + [Theory] + [InlineData("tt123456", false, null)] + [InlineData("tt1234567", true, "tt1234567")] + [InlineData("tt12345678", true, "tt12345678")] + [InlineData("https://www.imdb.com/title/tt123456", false, null)] + [InlineData("https://www.imdb.com/title/tt1234567", true, "tt1234567")] + [InlineData("https://www.imdb.com/title/tt12345678", true, "tt12345678")] + [InlineData(@"multiline\nhttps://www.imdb.com/title/tt1234567", true, "tt1234567")] + [InlineData(@"multiline\nhttps://www.imdb.com/title/tt12345678", true, "tt12345678")] + [InlineData("Jellyfin", false, null)] + [InlineData("tt1234567tt7654321", true, "tt1234567")] + [InlineData("tt12345678tt7654321", true, "tt12345678")] + public void Parse_Imdb(string text, bool shouldSucceed, string? imdbId) + { + var succeeded = ProviderIdParsers.TryParseImdbId(text, out string? parsedId); + Assert.Equal(shouldSucceed, succeeded); + Assert.Equal(imdbId, parsedId); + } + + [Theory] + [InlineData("https://www.themoviedb.org/movie/30287-fallo", true, "30287")] + [InlineData("themoviedb.org/movie/30287", true, "30287")] + [InlineData("https://www.themoviedb.org/movie/fallo-30287", false, null)] + [InlineData("https://www.themoviedb.org/tv/1668-friends", false, null)] + public void Parse_TmdbMovie(string text, bool shouldSucceed, string? tmdbId) + { + var succeeded = ProviderIdParsers.TryParseTmdbMovieId(text, out string? parsedId); + Assert.Equal(shouldSucceed, succeeded); + Assert.Equal(tmdbId, parsedId); + } + + [Theory] + [InlineData("https://www.themoviedb.org/tv/1668-friends", true, "1668")] + [InlineData("themoviedb.org/tv/1668", true, "1668")] + [InlineData("https://www.themoviedb.org/tv/friends-1668", false, null)] + [InlineData("https://www.themoviedb.org/movie/30287-fallo", false, null)] + public void Parse_TmdbSeries(string text, bool shouldSucceed, string? tmdbId) + { + var succeeded = ProviderIdParsers.TryParseTmdbSeriesId(text, out string? parsedId); + Assert.Equal(shouldSucceed, succeeded); + Assert.Equal(tmdbId, parsedId); + } + + [Theory] + [InlineData("https://www.thetvdb.com/?tab=series&id=121361", true, "121361")] + [InlineData("thetvdb.com/?tab=series&id=121361", true, "121361")] + [InlineData("thetvdb.com/?tab=series&id=Jellyfin121361", false, null)] + [InlineData("https://www.themoviedb.org/tv/1668-friends", false, null)] + public void Parse_Tvdb(string text, bool shouldSucceed, string? tvdbId) + { + var succeeded = ProviderIdParsers.TryParseTvdbId(text, out string? parsedId); + Assert.Equal(shouldSucceed, succeeded); + Assert.Equal(tvdbId, parsedId); + } + } +} From 37aa3e8735bf0089333cd19d6a0d03ed496e9f17 Mon Sep 17 00:00:00 2001 From: Vitorvlv Date: Wed, 17 Mar 2021 01:13:53 +0000 Subject: [PATCH 141/402] Translated using Weblate (Portuguese (Brazil)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/ --- Emby.Server.Implementations/Localization/Core/pt-BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index 5ec8f1e88..323dcced0 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -16,7 +16,7 @@ "Folders": "Pastas", "Genres": "Gêneros", "HeaderAlbumArtists": "Artistas do Álbum", - "HeaderContinueWatching": "Continuar Assistindo", + "HeaderContinueWatching": "Continuar assistindo", "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteEpisodes": "Episódios favoritos", From 151156f2271104277a575054aea9e5b0f92d6591 Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 17 Mar 2021 10:29:45 +0100 Subject: [PATCH 142/402] Clean the entity name for non-words before searching --- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 11 +++++++++-- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 3 ++- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 3 ++- MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index fcd8e614c..ca1af6c49 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -19,11 +20,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + private readonly ILibraryManager _libraryManager; - public TmdbBoxSetProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) + public TmdbBoxSetProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager, ILibraryManager libraryManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; + _libraryManager = libraryManager; } public string Name => TmdbUtils.ProviderName; @@ -83,7 +86,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets // We don't already have an Id, need to fetch it if (tmdbId <= 0) { - var searchResults = await _tmdbClientManager.SearchCollectionAsync(id.Name, language, cancellationToken).ConfigureAwait(false); + // ParseName is required here. + // Caller provides the filename with extension stripped and NOT the parsed filename + var parsedName = _libraryManager.ParseName(id.Name); + var cleanedName = TmdbUtils.CleanName(parsedName.Name); + var searchResults = await _tmdbClientManager.SearchCollectionAsync(cleanedName, language, cancellationToken).ConfigureAwait(false); if (searchResults != null && searchResults.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 2cd1ee717..4963777bc 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -140,7 +140,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies // ParseName is required here. // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); - var searchResults = await _tmdbClientManager.SearchMovieAsync(parsedName.Name, info.Year ?? parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var cleanedName = TmdbUtils.CleanName(parsedName.Name); + var searchResults = await _tmdbClientManager.SearchMovieAsync(cleanedName, info.Year ?? parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 74c2acf47..496e1ae25 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -189,7 +189,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // ParseName is required here. // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); - var searchResults = await _tmdbClientManager.SearchSeriesAsync(parsedName.Name, info.MetadataLanguage, info.Year ?? parsedName.Year ?? 0, cancellationToken).ConfigureAwait(false); + var cleanedName = TmdbUtils.CleanName(parsedName.Name); + var searchResults = await _tmdbClientManager.SearchSeriesAsync(cleanedName, info.MetadataLanguage, info.Year ?? parsedName.Year ?? 0, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index 0e8a5baab..15a44c7ed 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Text.RegularExpressions; using MediaBrowser.Model.Entities; using TMDbLib.Objects.General; @@ -12,6 +13,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// public static class TmdbUtils { + private static readonly Regex _nonWords = new (@"[\W_]+", RegexOptions.Compiled); + /// /// URL of the TMDB instance to use. /// @@ -42,6 +45,17 @@ namespace MediaBrowser.Providers.Plugins.Tmdb PersonType.Producer }; + /// + /// Cleans the name according to TMDb requirements. + /// + /// The name of the entity. + /// The cleaned name. + public static string CleanName(string name) + { + // TMDb expects a space separated list of words make sure that is the case + return _nonWords.Replace(name, " "); + } + /// /// Maps the TMDB provided roles for crew members to Jellyfin roles. /// From 12b8e29aefad3ad202769cdab9c6afc17c5687d9 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 17 Mar 2021 17:42:45 -0400 Subject: [PATCH 143/402] Fix duplicate permissions --- Jellyfin.Data/Entities/User.cs | 33 ++++++++++--------- .../Users/UserManager.cs | 3 ++ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 9aa809164..74331726c 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -73,9 +73,6 @@ namespace Jellyfin.Data.Entities PlayDefaultAudioTrack = true; SubtitleMode = SubtitlePlaybackMode.Default; SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups; - - AddDefaultPermissions(); - AddDefaultPreferences(); } /// @@ -483,18 +480,11 @@ namespace Jellyfin.Data.Entities return Array.IndexOf(GetPreferenceValues(PreferenceKind.GroupedFolders), id) != -1; } - private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date) - { - var localTime = date.ToLocalTime(); - var hour = localTime.TimeOfDay.TotalHours; - - return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) - && hour >= schedule.StartHour - && hour <= schedule.EndHour; - } - + /// + /// Initializes the default permissions for a user. Should only be called on user creation. + /// // TODO: make these user configurable? - private void AddDefaultPermissions() + public void AddDefaultPermissions() { Permissions.Add(new Permission(PermissionKind.IsAdministrator, false)); Permissions.Add(new Permission(PermissionKind.IsDisabled, false)); @@ -519,12 +509,25 @@ namespace Jellyfin.Data.Entities Permissions.Add(new Permission(PermissionKind.EnableRemoteControlOfOtherUsers, false)); } - private void AddDefaultPreferences() + /// + /// Initializes the default preferences. Should only be called on user creation. + /// + public void AddDefaultPreferences() { foreach (var val in Enum.GetValues(typeof(PreferenceKind)).Cast()) { Preferences.Add(new Preference(val, string.Empty)); } } + + private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date) + { + var localTime = date.ToLocalTime(); + var hour = localTime.TimeOfDay.TotalHours; + + return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) + && hour >= schedule.StartHour + && hour <= schedule.EndHour; + } } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index b400a0dd1..50d7612f2 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -190,6 +190,9 @@ namespace Jellyfin.Server.Implementations.Users InternalId = max + 1 }; + user.AddDefaultPermissions(); + user.AddDefaultPreferences(); + _users.Add(user.Id, user); return user; From 85da0b50e2e9686a0d660e2c2138709937c2fee2 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 17 Mar 2021 21:24:14 -0400 Subject: [PATCH 144/402] Fix user mocking --- .../Auth/CustomAuthenticationHandlerTests.cs | 2 ++ tests/Jellyfin.Api.Tests/TestHelpers.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs index ee20cc573..de03aa5f5 100644 --- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs @@ -128,6 +128,8 @@ namespace Jellyfin.Api.Tests.Auth { var authorizationInfo = _fixture.Create(); authorizationInfo.User = _fixture.Create(); + authorizationInfo.User.AddDefaultPermissions(); + authorizationInfo.User.AddDefaultPreferences(); authorizationInfo.User.SetPermission(PermissionKind.IsAdministrator, isAdmin); authorizationInfo.IsApiKey = false; diff --git a/tests/Jellyfin.Api.Tests/TestHelpers.cs b/tests/Jellyfin.Api.Tests/TestHelpers.cs index c1549561d..f9bca4146 100644 --- a/tests/Jellyfin.Api.Tests/TestHelpers.cs +++ b/tests/Jellyfin.Api.Tests/TestHelpers.cs @@ -29,6 +29,9 @@ namespace Jellyfin.Api.Tests typeof(DefaultAuthenticationProvider).FullName!, typeof(DefaultPasswordResetProvider).FullName!); + user.AddDefaultPermissions(); + user.AddDefaultPreferences(); + // Set administrator flag. user.SetPermission(PermissionKind.IsAdministrator, role.Equals(UserRoles.Administrator, StringComparison.OrdinalIgnoreCase)); From 840eeff2afb0faca7e65057276a1a3c7787102bf Mon Sep 17 00:00:00 2001 From: David Date: Thu, 18 Mar 2021 11:25:58 +0100 Subject: [PATCH 145/402] Apply suggestions from code review --- .../Providers/ProviderIdParsers.cs | 78 +++++++++++-------- .../Providers/ProviderIdParserTests.cs | 1 + 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs index 56e0112dd..bfe61a3f8 100644 --- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -10,6 +10,9 @@ namespace MediaBrowser.Common.Providers /// public static class ProviderIdParsers { + private const int ImdbMinNumbers = 7; + private const int ImdbMaxNumbers = 8; + /// /// Parses an IMDb id from a string. /// @@ -21,7 +24,8 @@ namespace MediaBrowser.Common.Providers var span = text.AsSpan(); var tt = "tt".AsSpan(); - while (true) + // imdb id is at least 9 chars (tt + 7 numbers) + while (span.Length >= 2 + ImdbMinNumbers) { var ttPos = span.IndexOf(tt); if (ttPos == -1) @@ -31,27 +35,28 @@ namespace MediaBrowser.Common.Providers } span = span.Slice(ttPos + tt.Length); - - int i = 0; - // IMDb id has a maximum of 8 digits - int max = span.Length > 8 ? 8 : span.Length; - for (; i < max; i++) + var i = 0; + for (; i < Math.Min(span.Length, ImdbMaxNumbers); i++) { var c = span[i]; - - if (c < '0' || c > '9') + if (!IsDigit(c)) { break; } } - // IMDb id has a minimum of 7 digits - if (i >= 7) + // skip if more than 8 digits + if (i <= ImdbMaxNumbers && i >= ImdbMinNumbers) { imdbId = string.Concat(tt, span.Slice(0, i)); return true; } + + span = span.Slice(i); } + + imdbId = default; + return false; } /// @@ -86,34 +91,39 @@ namespace MediaBrowser.Common.Providers var span = text.AsSpan(); var searchSpan = searchString.AsSpan(); - while (true) + var searchPos = span.IndexOf(searchSpan); + if (searchPos == -1) { - var searchPos = span.IndexOf(searchSpan); - if (searchPos == -1) + providerId = default; + return false; + } + + span = span.Slice(searchPos + searchSpan.Length); + + int i = 0; + for (; i < span.Length; i++) + { + var c = span[i]; + + if (!IsDigit(c)) { - providerId = default; - return false; - } - - span = span.Slice(searchPos + searchSpan.Length); - - int i = 0; - for (; i < span.Length; i++) - { - var c = span[i]; - - if (c < '0' || c > '9') - { - break; - } - } - - if (i >= 1) - { - providerId = span.Slice(0, i).ToString(); - return true; + break; } } + + if (i >= 1) + { + providerId = span.Slice(0, i).ToString(); + return true; + } + + providerId = default; + return false; + } + + private static bool IsDigit(char c) + { + return c >= '0' && c <= '9'; } } } diff --git a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs index a493dda64..cfe1ea86b 100644 --- a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs +++ b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs @@ -17,6 +17,7 @@ namespace Jellyfin.Common.Tests.Providers [InlineData("Jellyfin", false, null)] [InlineData("tt1234567tt7654321", true, "tt1234567")] [InlineData("tt12345678tt7654321", true, "tt12345678")] + [InlineData("tt123456789", true, "tt12345678")] public void Parse_Imdb(string text, bool shouldSucceed, string? imdbId) { var succeeded = ProviderIdParsers.TryParseImdbId(text, out string? parsedId); From 59641e5c766d8d4fc756ef327642ac04be24968a Mon Sep 17 00:00:00 2001 From: David Date: Thu, 18 Mar 2021 20:52:56 +0100 Subject: [PATCH 146/402] Use ReadOnlySpan and char.IsDigit --- .../Providers/ProviderIdParsers.cs | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs index bfe61a3f8..798667505 100644 --- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -19,27 +19,26 @@ namespace MediaBrowser.Common.Providers /// The text to parse. /// The parsed IMDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseImdbId(string text, [NotNullWhen(true)] out string? imdbId) + public static bool TryParseImdbId(ReadOnlySpan text, [NotNullWhen(true)] out string? imdbId) { - var span = text.AsSpan(); var tt = "tt".AsSpan(); // imdb id is at least 9 chars (tt + 7 numbers) - while (span.Length >= 2 + ImdbMinNumbers) + while (text.Length >= 2 + ImdbMinNumbers) { - var ttPos = span.IndexOf(tt); + var ttPos = text.IndexOf(tt); if (ttPos == -1) { imdbId = default; return false; } - span = span.Slice(ttPos + tt.Length); + text = text.Slice(ttPos + tt.Length); var i = 0; - for (; i < Math.Min(span.Length, ImdbMaxNumbers); i++) + for (; i < Math.Min(text.Length, ImdbMaxNumbers); i++) { - var c = span[i]; - if (!IsDigit(c)) + var c = text[i]; + if (!char.IsDigit(c)) { break; } @@ -48,11 +47,11 @@ namespace MediaBrowser.Common.Providers // skip if more than 8 digits if (i <= ImdbMaxNumbers && i >= ImdbMinNumbers) { - imdbId = string.Concat(tt, span.Slice(0, i)); + imdbId = string.Concat(tt, text.Slice(0, i)); return true; } - span = span.Slice(i); + text = text.Slice(i); } imdbId = default; @@ -65,7 +64,7 @@ namespace MediaBrowser.Common.Providers /// The text with the url to parse. /// The parsed TMDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseTmdbMovieId(string text, [NotNullWhen(true)] out string? tmdbId) + public static bool TryParseTmdbMovieId(ReadOnlySpan text, [NotNullWhen(true)] out string? tmdbId) => TryParseProviderId(text, "themoviedb.org/movie/", out tmdbId); /// @@ -74,7 +73,7 @@ namespace MediaBrowser.Common.Providers /// The text with the url to parse. /// The parsed TMDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseTmdbSeriesId(string text, [NotNullWhen(true)] out string? tmdbId) + public static bool TryParseTmdbSeriesId(ReadOnlySpan text, [NotNullWhen(true)] out string? tmdbId) => TryParseProviderId(text, "themoviedb.org/tv/", out tmdbId); /// @@ -83,29 +82,26 @@ namespace MediaBrowser.Common.Providers /// The text with the url to parse. /// The parsed TVDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseTvdbId(string text, [NotNullWhen(true)] out string? tvdbId) + public static bool TryParseTvdbId(ReadOnlySpan text, [NotNullWhen(true)] out string? tvdbId) => TryParseProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId); - private static bool TryParseProviderId(string text, string searchString, [NotNullWhen(true)] out string? providerId) + private static bool TryParseProviderId(ReadOnlySpan text, ReadOnlySpan searchString, [NotNullWhen(true)] out string? providerId) { - var span = text.AsSpan(); - var searchSpan = searchString.AsSpan(); - - var searchPos = span.IndexOf(searchSpan); + var searchPos = text.IndexOf(searchString); if (searchPos == -1) { providerId = default; return false; } - span = span.Slice(searchPos + searchSpan.Length); + text = text.Slice(searchPos + searchString.Length); int i = 0; - for (; i < span.Length; i++) + for (; i < text.Length; i++) { - var c = span[i]; + var c = text[i]; - if (!IsDigit(c)) + if (!char.IsDigit(c)) { break; } @@ -113,17 +109,12 @@ namespace MediaBrowser.Common.Providers if (i >= 1) { - providerId = span.Slice(0, i).ToString(); + providerId = text.Slice(0, i).ToString(); return true; } providerId = default; return false; } - - private static bool IsDigit(char c) - { - return c >= '0' && c <= '9'; - } } } From 9857c21717380261e11ec948c3520a673ffd83ae Mon Sep 17 00:00:00 2001 From: andrewthemeow Date: Fri, 19 Mar 2021 06:29:39 +0000 Subject: [PATCH 147/402] Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- Emby.Server.Implementations/Localization/Core/lt-LT.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index d4cb592ef..9920ef4d5 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -113,5 +113,9 @@ "TasksChannelsCategory": "Internetiniai Kanalai", "TasksApplicationCategory": "Programa", "TasksLibraryCategory": "Mediateka", - "TasksMaintenanceCategory": "Priežiūra" + "TasksMaintenanceCategory": "Priežiūra", + "TaskCleanActivityLog": "Švarus veiklos žurnalas", + "Undefined": "Neapibrėžtas", + "Forced": "Priverstas", + "Default": "Numatytas" } From 7685569480409f2703fc6ead32d093a08d783312 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 19 Mar 2021 12:34:21 +0100 Subject: [PATCH 148/402] Rollback char.IsDigit --- MediaBrowser.Common/Providers/ProviderIdParsers.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs index 798667505..7744124ea 100644 --- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Common.Providers for (; i < Math.Min(text.Length, ImdbMaxNumbers); i++) { var c = text[i]; - if (!char.IsDigit(c)) + if (!IsDigit(c)) { break; } @@ -101,7 +101,7 @@ namespace MediaBrowser.Common.Providers { var c = text[i]; - if (!char.IsDigit(c)) + if (!IsDigit(c)) { break; } @@ -116,5 +116,10 @@ namespace MediaBrowser.Common.Providers providerId = default; return false; } + + private static bool IsDigit(char c) + { + return c >= '0' && c <= '9'; + } } } From f61d18612b2e6c6e9a5dd4510331ac8d89a337d5 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sat, 20 Mar 2021 00:38:58 +0100 Subject: [PATCH 149/402] Fix directory traversal in the HlsSegmentController in a fairly rudimentary but working way. GHSL-2021-050: Issue 1,2,3 Arbitrary file read and directory traversal. The segment id's can probably just be verified to be an actual ID or to not contain any forward or backward slashes --- .../Controllers/HlsSegmentController.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index d0ed45acb..5a8542048 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -62,6 +62,13 @@ namespace Jellyfin.Api.Controllers // TODO: Deprecate with new iOS app var file = segmentId + Path.GetExtension(Request.Path); file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); + var transcodePath = _serverConfigurationManager.GetTranscodePath(); + file = Path.GetFullPath(Path.Combine(transcodePath, file)); + var fileDir = Path.GetDirectoryName(file); + if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath)) + { + return BadRequest("Invalid segment."); + } return FileStreamResponseHelpers.GetStaticFileResult(file, MimeTypes.GetMimeType(file)!, false, HttpContext); } @@ -82,6 +89,13 @@ namespace Jellyfin.Api.Controllers { var file = playlistId + Path.GetExtension(Request.Path); file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); + var transcodePath = _serverConfigurationManager.GetTranscodePath(); + file = Path.GetFullPath(Path.Combine(transcodePath, file)); + var fileDir = Path.GetDirectoryName(file); + if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath) || Path.GetExtension(file) != ".m3u8") + { + return BadRequest("Invalid segment."); + } return GetFileResult(file, file); } @@ -131,6 +145,12 @@ namespace Jellyfin.Api.Controllers var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath(); file = Path.Combine(transcodeFolderPath, file); + file = Path.GetFullPath(Path.Combine(transcodeFolderPath, file)); + var fileDir = Path.GetDirectoryName(file); + if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodeFolderPath)) + { + return BadRequest("Invalid segment."); + } var normalizedPlaylistId = playlistId; From 239a7156cc9c2c383aca1e7265ae4679666d5c85 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sat, 20 Mar 2021 00:46:59 +0100 Subject: [PATCH 150/402] Fix arbitrary image file reads in ImageByNameController GHSL-2021-050: Issue 4 Arbitrary image file read and directory traversal. --- .../Controllers/ImageByNameController.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/ImageByNameController.cs b/Jellyfin.Api/Controllers/ImageByNameController.cs index 198dbc51f..e1b808098 100644 --- a/Jellyfin.Api/Controllers/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/ImageByNameController.cs @@ -74,7 +74,7 @@ namespace Jellyfin.Api.Controllers : type; var path = BaseItem.SupportedImageExtensions - .Select(i => Path.Combine(_applicationPaths.GeneralPath, name, filename + i)) + .Select(i => Path.GetFullPath(Path.Combine(_applicationPaths.GeneralPath, name, filename + i))) .FirstOrDefault(System.IO.File.Exists); if (path == null) @@ -82,6 +82,11 @@ namespace Jellyfin.Api.Controllers return NotFound(); } + if (!path.StartsWith(_applicationPaths.GeneralPath)) + { + return BadRequest("Invalid image path."); + } + var contentType = MimeTypes.GetMimeType(path); return File(System.IO.File.OpenRead(path), contentType); } @@ -163,7 +168,8 @@ namespace Jellyfin.Api.Controllers /// 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.Combine(basePath, theme); + var themeFolder = Path.GetFullPath(Path.Combine(basePath, theme)); + if (Directory.Exists(themeFolder)) { var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, name + i)) @@ -171,12 +177,18 @@ namespace Jellyfin.Api.Controllers if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) { + if (!path.StartsWith(basePath)) + { + return BadRequest("Invalid image path."); + } + var contentType = MimeTypes.GetMimeType(path); + return PhysicalFile(path, contentType); } } - var allFolder = Path.Combine(basePath, "all"); + var allFolder = Path.GetFullPath(Path.Combine(basePath, "all")); if (Directory.Exists(allFolder)) { var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, name + i)) @@ -184,6 +196,11 @@ namespace Jellyfin.Api.Controllers if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) { + if (!path.StartsWith(basePath)) + { + return BadRequest("Invalid image path."); + } + var contentType = MimeTypes.GetMimeType(path); return PhysicalFile(path, contentType); } From 470305f75edc037653b68dd0614f73009219bdbd Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sat, 20 Mar 2021 01:07:09 +0100 Subject: [PATCH 151/402] Authenticated arbitrary file overwrite in SubtitleController -> SubtitleManager GHSL-2021-050: Issue 5 Arbitrary file overwrite. --- .../Subtitles/SubtitleManager.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index d4d79d27b..1f3d9acff 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -205,12 +205,30 @@ namespace MediaBrowser.Providers.Subtitles if (saveInMediaFolder) { - savePaths.Add(Path.Combine(video.ContainingFolderPath, saveFileName)); + var mediaFolderPath = Path.GetFullPath(Path.Combine(video.ContainingFolderPath, saveFileName)); + // TODO: Add some error handling to the API user: return BadRequest("Could not save subtitle, bad path."); + if (mediaFolderPath.StartsWith(video.ContainingFolderPath)) + { + savePaths.Add(mediaFolderPath); + } } - savePaths.Add(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); + var internalPath = Path.GetFullPath(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); - await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false); + // TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path."); + if (internalPath.StartsWith(video.GetInternalMetadataPath())) + { + savePaths.Add(internalPath); + } + + if (savePaths.Count > 0) + { + await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false); + } + else + { + _logger.LogError("An uploaded subtitle could not be saved because the resulting paths were invalid."); + } } } From 1f3aa3fe6f72f9b745dac7de5b6f8b877590017c Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sat, 20 Mar 2021 01:28:14 +0100 Subject: [PATCH 152/402] Apply review suggestions --- Jellyfin.Api/Controllers/HlsSegmentController.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 5a8542048..473bdc523 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -61,7 +61,6 @@ namespace Jellyfin.Api.Controllers { // TODO: Deprecate with new iOS app var file = segmentId + Path.GetExtension(Request.Path); - file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); var transcodePath = _serverConfigurationManager.GetTranscodePath(); file = Path.GetFullPath(Path.Combine(transcodePath, file)); var fileDir = Path.GetDirectoryName(file); @@ -88,7 +87,6 @@ namespace Jellyfin.Api.Controllers public ActionResult GetHlsPlaylistLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string playlistId) { var file = playlistId + Path.GetExtension(Request.Path); - file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); var transcodePath = _serverConfigurationManager.GetTranscodePath(); file = Path.GetFullPath(Path.Combine(transcodePath, file)); var fileDir = Path.GetDirectoryName(file); @@ -144,7 +142,6 @@ namespace Jellyfin.Api.Controllers var file = segmentId + Path.GetExtension(Request.Path); var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath(); - file = Path.Combine(transcodeFolderPath, file); file = Path.GetFullPath(Path.Combine(transcodeFolderPath, file)); var fileDir = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodeFolderPath)) From 32853ca2441d4b730258e6911faa56eba608dc1d Mon Sep 17 00:00:00 2001 From: LIAUD Date: Sat, 20 Mar 2021 20:15:19 +0100 Subject: [PATCH 153/402] Add 'group-title' channel parsing --- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 5 +++++ MediaBrowser.Controller/LiveTv/ChannelInfo.cs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index c4f173c7a..2af635492 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -133,6 +133,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts channel.ImageUrl = value; } + if (attributes.TryGetValue("group-title", out string groupTitle)) + { + channel.ChannelGroup = groupTitle; + } + channel.Name = GetChannelName(extInf, attributes); channel.Number = GetChannelNumber(extInf, attributes, mediaUrl); diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs index 44bd38b54..0f3f87847 100644 --- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs @@ -45,6 +45,12 @@ namespace MediaBrowser.Controller.LiveTv /// The type of the channel. public ChannelType ChannelType { get; set; } + /// + /// Gets or sets the group of the channel + /// + /// The group of the channel + public string ChannelGroup { get; set; } + /// /// Supply the image path if it can be accessed directly from the file system. /// From 72db3df605b7061fef28c99e42b7c11a06e8d995 Mon Sep 17 00:00:00 2001 From: LIAUD Date: Sat, 20 Mar 2021 20:31:38 +0100 Subject: [PATCH 154/402] Changed CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 954315f0e..9b1ac0673 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -17,6 +17,7 @@ - [bugfixin](https://github.com/bugfixin) - [chaosinnovator](https://github.com/chaosinnovator) - [ckcr4lyf](https://github.com/ckcr4lyf) + - [cocool97](https://github.com/cocool97) - [ConfusedPolarBear](https://github.com/ConfusedPolarBear) - [crankdoofus](https://github.com/crankdoofus) - [crobibero](https://github.com/crobibero) From 849ced470adf3ae122713e870e476b835c57d5ad Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 21 Mar 2021 03:26:51 +0100 Subject: [PATCH 155/402] Add StartupControllerTests --- .../Controllers/StartupControllerTests.cs | 61 +++++++++++++++++++ .../Jellyfin.Server.Integration.Tests.csproj | 1 + 2 files changed, 62 insertions(+) create mode 100644 tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs new file mode 100644 index 000000000..cf4b9dd71 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs @@ -0,0 +1,61 @@ +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Text.Json; +using System.Threading.Tasks; +using Jellyfin.Api.Models.StartupDtos; +using MediaBrowser.Common.Json; +using Xunit; +using Xunit.Priority; + +namespace Jellyfin.Server.Integration.Tests.Controllers +{ + public sealed class StartupControllerTests : IClassFixture + { + private readonly JellyfinApplicationFactory _factory; + private readonly JsonSerializerOptions _jsonOpions = JsonDefaults.Options; + + public StartupControllerTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + [Priority(0)] + public async Task GetStartupConfiguration_EditConfig_Success() + { + var client = _factory.CreateClient(); + + using var res0 = await client.GetAsync("/Startup/Configuration").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, res0.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, res0.Content.Headers.ContentType?.MediaType); + + var content0 = await res0.Content.ReadAsStreamAsync().ConfigureAwait(false); + _ = await JsonSerializer.DeserializeAsync(content0, _jsonOpions).ConfigureAwait(false); + + var newConfig = new StartupConfigurationDto() + { + UICulture = "NewCulture", + MetadataCountryCode = "be", + PreferredMetadataLanguage = "nl" + }; + + var req1 = JsonSerializer.SerializeToUtf8Bytes(newConfig, _jsonOpions); + using var reqContent1 = new ByteArrayContent(req1); + reqContent1.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + var res1 = await client.PostAsync("/Startup/Configuration", reqContent1).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, res1.StatusCode); + + var res2 = await client.GetAsync("/Startup/Configuration").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, res2.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, res2.Content.Headers.ContentType?.MediaType); + + var content2 = await res2.Content.ReadAsStreamAsync().ConfigureAwait(false); + var config2 = await JsonSerializer.DeserializeAsync(content2, _jsonOpions).ConfigureAwait(false); + Assert.Equal(newConfig.UICulture, config2!.UICulture); + Assert.Equal(newConfig.MetadataCountryCode, config2.MetadataCountryCode); + Assert.Equal(newConfig.PreferredMetadataLanguage, config2.PreferredMetadataLanguage); + } + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index b0a38736a..34cef9005 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -17,6 +17,7 @@ + From c5079ebed5187c40e19284772c710c41c4252cc9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 21 Mar 2021 03:59:31 +0100 Subject: [PATCH 156/402] Add tests for GetFirstUser, UpdateStartupUser and CompleteWizard --- .../Controllers/StartupControllerTests.cs | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs index cf4b9dd71..bb38db1be 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -11,6 +12,7 @@ using Xunit.Priority; namespace Jellyfin.Server.Integration.Tests.Controllers { + [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] public sealed class StartupControllerTests : IClassFixture { private readonly JellyfinApplicationFactory _factory; @@ -23,7 +25,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers [Fact] [Priority(0)] - public async Task GetStartupConfiguration_EditConfig_Success() + public async Task Configuration_EditConfig_Success() { var client = _factory.CreateClient(); @@ -57,5 +59,58 @@ namespace Jellyfin.Server.Integration.Tests.Controllers Assert.Equal(newConfig.MetadataCountryCode, config2.MetadataCountryCode); Assert.Equal(newConfig.PreferredMetadataLanguage, config2.PreferredMetadataLanguage); } + + [Fact] + [Priority(0)] + public async Task User_EditUser_Success() + { + var client = _factory.CreateClient(); + + using var res0 = await client.GetAsync("/Startup/User").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, res0.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, res0.Content.Headers.ContentType?.MediaType); + + var content0 = await res0.Content.ReadAsStreamAsync().ConfigureAwait(false); + var user = await JsonSerializer.DeserializeAsync(content0, _jsonOpions).ConfigureAwait(false); + + user!.Name = "NewName"; + user.Password = "NewPassword"; + + var req1 = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOpions); + using var reqContent1 = new ByteArrayContent(req1); + reqContent1.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + var res1 = await client.PostAsync("/Startup/User", reqContent1).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, res1.StatusCode); + + var res2 = await client.GetAsync("/Startup/User").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, res2.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, res2.Content.Headers.ContentType?.MediaType); + + var content2 = await res2.Content.ReadAsStreamAsync().ConfigureAwait(false); + var user2 = await JsonSerializer.DeserializeAsync(content2, _jsonOpions).ConfigureAwait(false); + Assert.Equal(user.Name, user2!.Name); + Assert.NotEmpty(user2.Password); + Assert.NotEqual(user.Password, user2.Password); + } + + [Fact] + [Priority(1)] + public async Task CompleteWizard_Success() + { + var client = _factory.CreateClient(); + + var res = await client.PostAsync("/Startup/Complete", new ByteArrayContent(Array.Empty())).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, res.StatusCode); + } + + [Fact] + [Priority(2)] + public async Task GetFirstUser_CompleteWizard_Unauthorized() + { + var client = _factory.CreateClient(); + + using var res0 = await client.GetAsync("/Startup/User").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.Unauthorized, res0.StatusCode); + } } } From fcb070abf702c587fc98183253d56ec7f501eaa6 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Mon, 22 Mar 2021 18:36:04 +0800 Subject: [PATCH 157/402] disable auto rotation for some HWA methods --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e5877a484..b0cf47c7a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -541,6 +541,8 @@ namespace MediaBrowser.Controller.MediaEncoding .Append(encodingOptions.VaapiDevice) .Append(' '); } + + arg.Append("-autorotate 0"); } if (state.IsVideoRequest @@ -585,6 +587,8 @@ namespace MediaBrowser.Controller.MediaEncoding .Append("-init_hw_device qsv@va ") .Append("-hwaccel_output_format vaapi "); } + + arg.Append("-autorotate 0 "); } } @@ -592,7 +596,8 @@ namespace MediaBrowser.Controller.MediaEncoding && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecDecoder) { - arg.Append("-hwaccel_output_format cuda "); + arg.Append("-hwaccel_output_format cuda ") + .Append("-autorotate 0 "); } if (state.IsVideoRequest From a6bc191607675cd5676c6d1dceaf0ad37ac7e508 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 22 Mar 2021 19:25:41 +0800 Subject: [PATCH 158/402] Apply suggestions from code review Co-authored-by: Claus Vium --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b0cf47c7a..1df040a26 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -596,8 +596,7 @@ namespace MediaBrowser.Controller.MediaEncoding && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecDecoder) { - arg.Append("-hwaccel_output_format cuda ") - .Append("-autorotate 0 "); + arg.Append("-hwaccel_output_format cuda -autorotate 0 "); } if (state.IsVideoRequest From fab4bf184e27eba59566702d20ed352f75f0418b Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 22 Mar 2021 19:47:05 +0800 Subject: [PATCH 159/402] Apply suggestions from code review Co-authored-by: Claus Vium --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 1df040a26..09080e7b2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -542,7 +542,7 @@ namespace MediaBrowser.Controller.MediaEncoding .Append(' '); } - arg.Append("-autorotate 0"); + arg.Append("-autorotate 0 "); } if (state.IsVideoRequest From 5253483ce4213a5f32fc15210ef61b2df9915185 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 22 Mar 2021 13:49:00 +0100 Subject: [PATCH 160/402] Improve naming --- .../Controllers/StartupControllerTests.cs | 107 +++++++++--------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs index bb38db1be..169a5a6c5 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs @@ -16,7 +16,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers public sealed class StartupControllerTests : IClassFixture { private readonly JellyfinApplicationFactory _factory; - private readonly JsonSerializerOptions _jsonOpions = JsonDefaults.Options; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; public StartupControllerTests(JellyfinApplicationFactory factory) { @@ -24,93 +24,96 @@ namespace Jellyfin.Server.Integration.Tests.Controllers } [Fact] - [Priority(0)] + [Priority(-2)] public async Task Configuration_EditConfig_Success() { var client = _factory.CreateClient(); - using var res0 = await client.GetAsync("/Startup/Configuration").ConfigureAwait(false); - Assert.Equal(HttpStatusCode.OK, res0.StatusCode); - Assert.Equal(MediaTypeNames.Application.Json, res0.Content.Headers.ContentType?.MediaType); - - var content0 = await res0.Content.ReadAsStreamAsync().ConfigureAwait(false); - _ = await JsonSerializer.DeserializeAsync(content0, _jsonOpions).ConfigureAwait(false); - - var newConfig = new StartupConfigurationDto() + var config = new StartupConfigurationDto() { UICulture = "NewCulture", MetadataCountryCode = "be", PreferredMetadataLanguage = "nl" }; - var req1 = JsonSerializer.SerializeToUtf8Bytes(newConfig, _jsonOpions); - using var reqContent1 = new ByteArrayContent(req1); - reqContent1.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); - var res1 = await client.PostAsync("/Startup/Configuration", reqContent1).ConfigureAwait(false); - Assert.Equal(HttpStatusCode.NoContent, res1.StatusCode); + using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(config, _jsonOptions)); + postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + using var postResponse = await client.PostAsync("/Startup/Configuration", postContent).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, postResponse.StatusCode); - var res2 = await client.GetAsync("/Startup/Configuration").ConfigureAwait(false); - Assert.Equal(HttpStatusCode.OK, res2.StatusCode); - Assert.Equal(MediaTypeNames.Application.Json, res2.Content.Headers.ContentType?.MediaType); + using var getResponse = await client.GetAsync("/Startup/Configuration").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, getResponse.Content.Headers.ContentType?.MediaType); - var content2 = await res2.Content.ReadAsStreamAsync().ConfigureAwait(false); - var config2 = await JsonSerializer.DeserializeAsync(content2, _jsonOpions).ConfigureAwait(false); - Assert.Equal(newConfig.UICulture, config2!.UICulture); - Assert.Equal(newConfig.MetadataCountryCode, config2.MetadataCountryCode); - Assert.Equal(newConfig.PreferredMetadataLanguage, config2.PreferredMetadataLanguage); + using var responseStream = await getResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var newConfig = await JsonSerializer.DeserializeAsync(responseStream, _jsonOptions).ConfigureAwait(false); + Assert.Equal(config.UICulture, newConfig!.UICulture); + Assert.Equal(config.MetadataCountryCode, newConfig.MetadataCountryCode); + Assert.Equal(config.PreferredMetadataLanguage, newConfig.PreferredMetadataLanguage); } [Fact] - [Priority(0)] + [Priority(-2)] + public async Task User_DefaultUser_NameWithoutPassword() + { + var client = _factory.CreateClient(); + + using var response = await client.GetAsync("/Startup/User").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType); + + using var contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var user = await JsonSerializer.DeserializeAsync(contentStream, _jsonOptions).ConfigureAwait(false); + Assert.NotEmpty(user!.Name); + Assert.Null(user.Password); + } + + [Fact] + [Priority(-1)] public async Task User_EditUser_Success() { var client = _factory.CreateClient(); - using var res0 = await client.GetAsync("/Startup/User").ConfigureAwait(false); - Assert.Equal(HttpStatusCode.OK, res0.StatusCode); - Assert.Equal(MediaTypeNames.Application.Json, res0.Content.Headers.ContentType?.MediaType); + var user = new StartupUserDto() + { + Name = "NewName", + Password = "NewPassword" + }; - var content0 = await res0.Content.ReadAsStreamAsync().ConfigureAwait(false); - var user = await JsonSerializer.DeserializeAsync(content0, _jsonOpions).ConfigureAwait(false); + using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(user, _jsonOptions)); + postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + var postResponse = await client.PostAsync("/Startup/User", postContent).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, postResponse.StatusCode); - user!.Name = "NewName"; - user.Password = "NewPassword"; + var getResponse = await client.GetAsync("/Startup/User").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, getResponse.Content.Headers.ContentType?.MediaType); - var req1 = JsonSerializer.SerializeToUtf8Bytes(user, _jsonOpions); - using var reqContent1 = new ByteArrayContent(req1); - reqContent1.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); - var res1 = await client.PostAsync("/Startup/User", reqContent1).ConfigureAwait(false); - Assert.Equal(HttpStatusCode.NoContent, res1.StatusCode); - - var res2 = await client.GetAsync("/Startup/User").ConfigureAwait(false); - Assert.Equal(HttpStatusCode.OK, res2.StatusCode); - Assert.Equal(MediaTypeNames.Application.Json, res2.Content.Headers.ContentType?.MediaType); - - var content2 = await res2.Content.ReadAsStreamAsync().ConfigureAwait(false); - var user2 = await JsonSerializer.DeserializeAsync(content2, _jsonOpions).ConfigureAwait(false); - Assert.Equal(user.Name, user2!.Name); - Assert.NotEmpty(user2.Password); - Assert.NotEqual(user.Password, user2.Password); + var contentStream = await getResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var newUser = await JsonSerializer.DeserializeAsync(contentStream, _jsonOptions).ConfigureAwait(false); + Assert.Equal(user.Name, newUser!.Name); + Assert.NotEmpty(newUser.Password); + Assert.NotEqual(user.Password, newUser.Password); } [Fact] - [Priority(1)] + [Priority(0)] public async Task CompleteWizard_Success() { var client = _factory.CreateClient(); - var res = await client.PostAsync("/Startup/Complete", new ByteArrayContent(Array.Empty())).ConfigureAwait(false); - Assert.Equal(HttpStatusCode.NoContent, res.StatusCode); + var response = await client.PostAsync("/Startup/Complete", new ByteArrayContent(Array.Empty())).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); } [Fact] - [Priority(2)] + [Priority(1)] public async Task GetFirstUser_CompleteWizard_Unauthorized() { var client = _factory.CreateClient(); - using var res0 = await client.GetAsync("/Startup/User").ConfigureAwait(false); - Assert.Equal(HttpStatusCode.Unauthorized, res0.StatusCode); + using var response = await client.GetAsync("/Startup/User").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } } From 0853d1265c99a2e8614aa0c7a584dff541484e19 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Tue, 23 Mar 2021 00:59:57 +0800 Subject: [PATCH 161/402] Disable auto rotation for some HWA methods (#5586) Co-authored-by: Claus Vium --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e5877a484..09080e7b2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -541,6 +541,8 @@ namespace MediaBrowser.Controller.MediaEncoding .Append(encodingOptions.VaapiDevice) .Append(' '); } + + arg.Append("-autorotate 0 "); } if (state.IsVideoRequest @@ -585,6 +587,8 @@ namespace MediaBrowser.Controller.MediaEncoding .Append("-init_hw_device qsv@va ") .Append("-hwaccel_output_format vaapi "); } + + arg.Append("-autorotate 0 "); } } @@ -592,7 +596,7 @@ namespace MediaBrowser.Controller.MediaEncoding && string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecDecoder) { - arg.Append("-hwaccel_output_format cuda "); + arg.Append("-hwaccel_output_format cuda -autorotate 0 "); } if (state.IsVideoRequest From 7fa525c83b7573e61124fa1c64a3b27569e66b6d Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 22 Mar 2021 17:04:09 +0000 Subject: [PATCH 162/402] Added more tests --- Jellyfin.sln | 17 +++++++++++------ .../NetworkParseTests.cs | 11 +++++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Jellyfin.sln b/Jellyfin.sln index 7b81f4346..9d5cd98e2 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30503.244 MinimumVisualStudioVersion = 10.0.40219.1 @@ -70,15 +71,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jell EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Tests", "tests\Jellyfin.Server.Tests\Jellyfin.Server.Tests.csproj", "{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -210,6 +211,10 @@ Global {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 19f0faa8f..de81ad0a6 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -518,7 +518,9 @@ namespace Jellyfin.Networking.Tests [Theory] [InlineData("185.10.10.10,200.200.200.200", "79.2.3.4", true)] [InlineData("185.10.10.10", "185.10.10.10", false)] - public void HasRemoteAccess_GivenNonEmptyWhitelist_AllowsOnlyIpsInWhitelist(string addresses, string remoteIp, bool denied) + [InlineData("", "100.100.100.100", false)] + + public void HasRemoteAccess_GivenWhitelist_AllowsOnlyIpsInWhitelist(string addresses, string remoteIp, bool denied) { // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. // If left blank, all remote addresses will be allowed. @@ -534,9 +536,10 @@ namespace Jellyfin.Networking.Tests } [Theory] - [InlineData("185.10.10.10", "79.2.3.4", false)] // blacklist - [InlineData("185.10.10.10", "185.10.10.10", true)] // blacklist - public void HasRemoteAccess_GivenNonEmptyBlacklist_BlacklistTheIps(string addresses, string remoteIp, bool denied) + [InlineData("185.10.10.10", "79.2.3.4", false)] + [InlineData("185.10.10.10", "185.10.10.10", true)] + [InlineData("", "100.100.100.100", false)] + public void HasRemoteAccess_GivenBlacklist_BlacklistTheIps(string addresses, string remoteIp, bool denied) { // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely. // If left blank, all remote addresses will be allowed. From 4bd345fbabd7832bcb91a920ec81dc14a7420218 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 22 Mar 2021 17:21:12 +0000 Subject: [PATCH 163/402] DLNA Exception catching --- Emby.Dlna/PlayTo/Device.cs | 41 +++++++++++++++---- Emby.Dlna/PlayTo/PlayToManager.cs | 5 +++ Emby.Dlna/PlayTo/SsdpHttpClient.cs | 15 +++++-- .../NetworkParseTests.cs | 4 +- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 7bf7047fb..abd99bbc3 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -219,7 +219,7 @@ namespace Emby.Dlna.PlayTo { var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); - var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute"); + var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetMute"); if (command == null) { return false; @@ -259,7 +259,7 @@ namespace Emby.Dlna.PlayTo { var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); - var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume"); + var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume"); if (command == null) { return; @@ -290,7 +290,7 @@ namespace Emby.Dlna.PlayTo { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); - var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek"); + var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Seek"); if (command == null) { return; @@ -323,7 +323,7 @@ namespace Emby.Dlna.PlayTo _logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header); - var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI"); + var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI"); if (command == null) { return; @@ -403,6 +403,10 @@ namespace Emby.Dlna.PlayTo public async Task SetPlay(CancellationToken cancellationToken) { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); + if (avCommands == null) + { + return; + } await SetPlay(avCommands, cancellationToken).ConfigureAwait(false); @@ -413,7 +417,7 @@ namespace Emby.Dlna.PlayTo { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); - var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop"); + var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Stop"); if (command == null) { return; @@ -437,7 +441,7 @@ namespace Emby.Dlna.PlayTo { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); - var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause"); + var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Pause"); if (command == null) { return; @@ -565,7 +569,7 @@ namespace Emby.Dlna.PlayTo var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); - var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume"); + var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume"); if (command == null) { return; @@ -615,7 +619,7 @@ namespace Emby.Dlna.PlayTo var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); - var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute"); + var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "GetMute"); if (command == null) { return; @@ -702,6 +706,10 @@ namespace Emby.Dlna.PlayTo } var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); + if (rendererCommands == null) + { + return null; + } var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync( Properties.BaseUrl, @@ -770,6 +778,11 @@ namespace Emby.Dlna.PlayTo var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); + if (rendererCommands == null) + { + return (false, null); + } + var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync( Properties.BaseUrl, service, @@ -951,6 +964,10 @@ namespace Emby.Dlna.PlayTo var httpClient = new SsdpHttpClient(_httpClientFactory); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); + if (document == null) + { + return null; + } AvCommands = TransportCommands.Create(document); return AvCommands; @@ -979,6 +996,10 @@ namespace Emby.Dlna.PlayTo var httpClient = new SsdpHttpClient(_httpClientFactory); _logger.LogDebug("Dlna Device.GetRenderingProtocolAsync"); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); + if (document == null) + { + return null; + } RendererCommands = TransportCommands.Create(document); return RendererCommands; @@ -1010,6 +1031,10 @@ namespace Emby.Dlna.PlayTo var ssdpHttpClient = new SsdpHttpClient(httpClientFactory); var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false); + if (document == null) + { + return null; + } var friendlyNames = new List(); diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index a6793a708..8272e505a 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -178,6 +178,11 @@ namespace Emby.Dlna.PlayTo if (controller == null) { var device = await Device.CreateuPnpDeviceAsync(uri, _httpClientFactory, _logger, cancellationToken).ConfigureAwait(false); + if (device == null) + { + _logger.LogError("Ignoring device as xml response is invalid."); + return; + } string deviceName = device.Properties.Name; diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index b7643fb27..e750f5bbc 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -94,10 +94,17 @@ namespace Emby.Dlna.PlayTo options.Headers.TryAddWithoutValidation("FriendlyName.DLNA.ORG", FriendlyName); using var response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - return await XDocument.LoadAsync( - stream, - LoadOptions.PreserveWhitespace, - cancellationToken).ConfigureAwait(false); + try + { + return await XDocument.LoadAsync( + stream, + LoadOptions.PreserveWhitespace, + cancellationToken).ConfigureAwait(false); + } + catch + { + return null; + } } private async Task PostSoapDataAsync( diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index c3469035e..28b5a4691 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Networking.Tests /// /// Checks IP address formats. /// - /// + /// IP Address. [Theory] [InlineData("127.0.0.1")] [InlineData("127.0.0.1:123")] @@ -107,7 +107,7 @@ namespace Jellyfin.Networking.Tests /// /// Checks IP address formats. /// - /// + /// IP Address. [Theory] [InlineData("127.0.0.1")] [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] From 4637bbc723b2462ca01cb6cc5bd7e5e8b70b10c2 Mon Sep 17 00:00:00 2001 From: Kenneth SB Date: Mon, 22 Mar 2021 15:24:00 +0000 Subject: [PATCH 164/402] Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- Emby.Server.Implementations/Localization/Core/da.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 4ee4eb989..051d6d009 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -1,5 +1,5 @@ { - "Albums": "Albums", + "Albums": "Albummer", "AppDeviceValues": "App: {0}, Enhed: {1}", "Application": "Applikation", "Artists": "Kunstnere", From 7dedeb6c795cb7764ec1cd1d2cd943f5c5506847 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 22 Mar 2021 20:53:55 +0100 Subject: [PATCH 165/402] change HLS endpoint defaults to false --- Emby.Dlna/PlayTo/PlayToController.cs | 2 +- Jellyfin.Api/Controllers/AudioController.cs | 24 +++---- .../Controllers/DynamicHlsController.cs | 72 +++++++++---------- .../Controllers/UniversalAudioController.cs | 8 +-- .../Controllers/VideoHlsController.cs | 12 ++-- Jellyfin.Api/Controllers/VideosController.cs | 12 ++-- MediaBrowser.Model/Dlna/StreamInfo.cs | 2 +- 7 files changed, 66 insertions(+), 66 deletions(-) diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 5abc1bc13..6021da51d 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -945,7 +945,7 @@ namespace Emby.Dlna.PlayTo request.LiveStreamId = values.GetValueOrDefault("LiveStreamId"); // Be careful, IsDirectStream==true by default (Static != false or not in query). - // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? true. + // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? false. request.IsDirectStream = !string.Equals("false", values.GetValueOrDefault("Static"), StringComparison.OrdinalIgnoreCase); request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex"); diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 8b1813b20..a6e70e72d 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers { Id = itemId, Container = container, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -168,7 +168,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -177,13 +177,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, @@ -309,7 +309,7 @@ namespace Jellyfin.Api.Controllers { Id = itemId, Container = container, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -333,7 +333,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -342,13 +342,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index f6c23c5aa..c4e75fe85 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -225,7 +225,7 @@ namespace Jellyfin.Api.Controllers var streamingRequest = new HlsVideoRequestDto { Id = itemId, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -249,7 +249,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -258,13 +258,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, @@ -392,7 +392,7 @@ namespace Jellyfin.Api.Controllers var streamingRequest = new HlsAudioRequestDto { Id = itemId, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -416,7 +416,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -425,13 +425,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, @@ -555,7 +555,7 @@ namespace Jellyfin.Api.Controllers var streamingRequest = new VideoRequestDto { Id = itemId, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -579,7 +579,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -588,13 +588,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, @@ -720,7 +720,7 @@ namespace Jellyfin.Api.Controllers var streamingRequest = new StreamingRequestDto { Id = itemId, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -744,7 +744,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -753,13 +753,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, @@ -890,7 +890,7 @@ namespace Jellyfin.Api.Controllers { Id = itemId, Container = container, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -914,7 +914,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -923,13 +923,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, @@ -1062,7 +1062,7 @@ namespace Jellyfin.Api.Controllers { Id = itemId, Container = container, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -1086,7 +1086,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -1095,13 +1095,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 0c2e6f19f..dcdd8b367 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -219,10 +219,10 @@ namespace Jellyfin.Api.Controllers AudioBitRate = audioBitRate ?? maxStreamingBitrate, StartTimeTicks = startTimeTicks, SubtitleMethod = SubtitleDeliveryMethod.Hls, - RequireAvc = true, - DeInterlace = true, - RequireNonAnamorphic = true, - EnableMpegtsM2TsMode = true, + RequireAvc = false, + DeInterlace = false, + RequireNonAnamorphic = false, + EnableMpegtsM2TsMode = false, TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(',', mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()), Context = EncodingContext.Static, StreamOptions = new Dictionary(), diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 620eef568..e95410d02 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -223,7 +223,7 @@ namespace Jellyfin.Api.Controllers { Id = itemId, Container = container, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -247,7 +247,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -256,13 +256,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 99654e7b0..699ca5327 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -386,7 +386,7 @@ namespace Jellyfin.Api.Controllers { Id = itemId, Container = container, - Static = @static ?? true, + Static = @static ?? false, Params = @params, Tag = tag, DeviceProfileId = deviceProfileId, @@ -410,7 +410,7 @@ namespace Jellyfin.Api.Controllers Level = level, Framerate = framerate, MaxFramerate = maxFramerate, - CopyTimestamps = copyTimestamps ?? true, + CopyTimestamps = copyTimestamps ?? false, StartTimeTicks = startTimeTicks, Width = width, Height = height, @@ -419,13 +419,13 @@ namespace Jellyfin.Api.Controllers SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, - RequireAvc = requireAvc ?? true, - DeInterlace = deInterlace ?? true, - RequireNonAnamorphic = requireNonAnamorphic ?? true, + RequireAvc = requireAvc ?? false, + DeInterlace = deInterlace ?? false, + RequireNonAnamorphic = requireNonAnamorphic ?? false, TranscodingMaxAudioChannels = transcodingMaxAudioChannels, CpuCoreLimit = cpuCoreLimit, LiveStreamId = liveStreamId, - EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true, + EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? false, VideoCodec = videoCodec, SubtitleCodec = subtitleCodec, TranscodeReasons = transcodeReasons, diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 29da5d9e7..475bea4cd 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -631,7 +631,7 @@ namespace MediaBrowser.Model.Dlna } // Be careful, IsDirectStream==true by default (Static != false or not in query). - // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? true. + // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? false. if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) && string.Equals(pair.Value, "true", StringComparison.OrdinalIgnoreCase)) { From 74e14b4ca591b9043435ee1065827101379b1318 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 22 Mar 2021 22:34:47 +0100 Subject: [PATCH 166/402] fix isdirectstream default --- Emby.Dlna/PlayTo/PlayToController.cs | 6 +----- MediaBrowser.Model/Dlna/StreamInfo.cs | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 6021da51d..25ba18add 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -943,11 +943,7 @@ namespace Emby.Dlna.PlayTo request.DeviceId = values.GetValueOrDefault("DeviceId"); request.MediaSourceId = values.GetValueOrDefault("MediaSourceId"); request.LiveStreamId = values.GetValueOrDefault("LiveStreamId"); - - // Be careful, IsDirectStream==true by default (Static != false or not in query). - // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? false. - request.IsDirectStream = !string.Equals("false", values.GetValueOrDefault("Static"), StringComparison.OrdinalIgnoreCase); - + request.IsDirectStream = string.Equals("true", values.GetValueOrDefault("Static"), StringComparison.OrdinalIgnoreCase); request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex"); request.SubtitleStreamIndex = GetIntValue(values, "SubtitleStreamIndex"); request.StartPositionTicks = GetLongValue(values, "StartPositionTicks"); diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 475bea4cd..252872847 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -630,10 +630,8 @@ namespace MediaBrowser.Model.Dlna continue; } - // Be careful, IsDirectStream==true by default (Static != false or not in query). - // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? false. if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) && - string.Equals(pair.Value, "true", StringComparison.OrdinalIgnoreCase)) + string.Equals(pair.Value, "false", StringComparison.OrdinalIgnoreCase)) { continue; } From 6765f6ab172bcb5a6a98ba84087c5b51288f05d7 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 23 Mar 2021 00:12:14 +0000 Subject: [PATCH 167/402] fix compilation --- .../Middleware/IpBasedAccessValidationMiddleware.cs | 3 +-- tests/Jellyfin.Networking.Tests/NetworkParseTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index 96e6fd834..7d92bd7d3 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -29,9 +29,8 @@ namespace Jellyfin.Server.Middleware /// /// The current HTTP context. /// The network manager. - /// The server configuration manager. /// The async task. - public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager) + public async Task Invoke(HttpContext httpContext, INetworkManager networkManager) { if (httpContext.IsLocal()) { diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index de81ad0a6..5d22b4d33 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Networking.Tests /// /// Checks IP address formats. /// - /// + /// IP Address. [Theory] [InlineData("127.0.0.1")] [InlineData("127.0.0.1:123")] @@ -107,7 +107,7 @@ namespace Jellyfin.Networking.Tests /// /// Checks IP address formats. /// - /// + /// IP Address. [Theory] [InlineData("127.0.0.1")] [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] From 572600b38ec560ee57e9954e2967014a042a1c06 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 23 Mar 2021 15:47:55 +0100 Subject: [PATCH 168/402] Use conditional operator instead of if/else block --- .../Parsers/MovieNfoParserTests.cs | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 76e10b451..4c556b4d6 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -56,26 +56,15 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers .Returns(new UserItemData()); var directoryService = new Mock(); - if (MediaBrowser.Common.System.OperatingSystem.Id != OperatingSystemId.Windows) + _localImageFileMetadata = new FileSystemMetadata() { - _localImageFileMetadata = new FileSystemMetadata() - { - Exists = true, - FullName = "/media/movies/Justice League (2017).jpg" - }; - directoryService.Setup(x => x.GetFile(_localImageFileMetadata.FullName)) - .Returns(_localImageFileMetadata); - } - else - { - _localImageFileMetadata = new FileSystemMetadata() - { - Exists = true, - FullName = "C:\\media\\movies\\Justice League (2017).jpg" - }; - directoryService.Setup(x => x.GetFile(_localImageFileMetadata.FullName)) - .Returns(_localImageFileMetadata); - } + Exists = true, + FullName = MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows ? + "C:\\media\\movies\\Justice League (2017).jpg" + : "/media/movies/Justice League (2017).jpg" + }; + directoryService.Setup(x => x.GetFile(_localImageFileMetadata.FullName)) + .Returns(_localImageFileMetadata); _userDataManager = userData.Object; _parser = new MovieNfoParser( From 19e4ef82dda1cbba9d533047950afb42c425749a Mon Sep 17 00:00:00 2001 From: David Date: Tue, 23 Mar 2021 17:16:10 +0100 Subject: [PATCH 169/402] Remove conversion from IPAddress to string to IPAddress --- .../HttpServer/Security/SessionContext.cs | 2 +- Jellyfin.Api/Controllers/UserController.cs | 25 +++++++++++++------ Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 2 +- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 5 ++-- Jellyfin.Api/Helpers/RequestHelpers.cs | 2 +- .../Extensions/HttpContextExtensions.cs | 4 +-- .../LocalAccessHandlerTests.cs | 3 ++- 7 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs index 040b6b9e4..dd77b45d8 100644 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.HttpServer.Security var authorization = _authContext.GetAuthorizationInfo(requestContext); var user = authorization.User; - return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp(), user); + return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp().ToString(), user); } public SessionInfo GetSession(object requestContext) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 43ee309b7..3c0d2aca1 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -21,6 +21,7 @@ using MediaBrowser.Model.Users; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Controllers { @@ -36,6 +37,7 @@ namespace Jellyfin.Api.Controllers private readonly IDeviceManager _deviceManager; private readonly IAuthorizationContext _authContext; private readonly IServerConfigurationManager _config; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. @@ -46,13 +48,15 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public UserController( IUserManager userManager, ISessionManager sessionManager, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext, - IServerConfigurationManager config) + IServerConfigurationManager config, + ILogger logger) { _userManager = userManager; _sessionManager = sessionManager; @@ -60,6 +64,7 @@ namespace Jellyfin.Api.Controllers _deviceManager = deviceManager; _authContext = authContext; _config = config; + _logger = logger; } /// @@ -118,7 +123,7 @@ namespace Jellyfin.Api.Controllers return NotFound("User not found"); } - var result = _userManager.GetUserDto(user, HttpContext.GetNormalizedRemoteIp()); + var result = _userManager.GetUserDto(user, HttpContext.GetNormalizedRemoteIp().ToString()); return result; } @@ -204,7 +209,7 @@ namespace Jellyfin.Api.Controllers DeviceName = auth.Device, Password = request.Pw, PasswordSha1 = request.Password, - RemoteEndPoint = HttpContext.GetNormalizedRemoteIp(), + RemoteEndPoint = HttpContext.GetNormalizedRemoteIp().ToString(), Username = request.Username }).ConfigureAwait(false); @@ -291,7 +296,7 @@ namespace Jellyfin.Api.Controllers user.Username, request.CurrentPw, request.CurrentPw, - HttpContext.GetNormalizedRemoteIp(), + HttpContext.GetNormalizedRemoteIp().ToString(), false).ConfigureAwait(false); if (success == null) @@ -483,7 +488,7 @@ namespace Jellyfin.Api.Controllers await _userManager.ChangePassword(newUser, request.Password).ConfigureAwait(false); } - var result = _userManager.GetUserDto(newUser, HttpContext.GetNormalizedRemoteIp()); + var result = _userManager.GetUserDto(newUser, HttpContext.GetNormalizedRemoteIp().ToString()); return result; } @@ -498,8 +503,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public async Task> ForgotPassword([FromBody, Required] ForgotPasswordDto forgotPasswordRequest) { + var ip = HttpContext.GetNormalizedRemoteIp(); var isLocal = HttpContext.IsLocal() - || _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIp()); + || _networkManager.IsInLocalNetwork(ip); + + if (isLocal) + { + _logger.LogWarning("Password reset proccess initiated from outside the local network with IP: {IP}", ip); + } var result = await _userManager.StartForgotPasswordProcess(forgotPasswordRequest.EnteredUsername, isLocal).ConfigureAwait(false); @@ -581,7 +592,7 @@ namespace Jellyfin.Api.Controllers var result = users .OrderBy(u => u.Username) - .Select(i => _userManager.GetUserDto(i, HttpContext.GetNormalizedRemoteIp())); + .Select(i => _userManager.GetUserDto(i, HttpContext.GetNormalizedRemoteIp().ToString())); return result; } diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 16380f0bb..751b48682 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -434,7 +434,7 @@ namespace Jellyfin.Api.Helpers } } - private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, string ipAddress) + private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming, IPAddress ipAddress) { // Within the local network this will likely do more harm than good. if (_networkManager.IsInLocalNetwork(ipAddress)) diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index ce6740fc9..a2d0e7030 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Net; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -179,7 +180,7 @@ namespace Jellyfin.Api.Helpers bool enableTranscoding, bool allowVideoStreamCopy, bool allowAudioStreamCopy, - string ipAddress) + IPAddress ipAddress) { var streamBuilder = new StreamBuilder(_mediaEncoder, _logger); @@ -551,7 +552,7 @@ namespace Jellyfin.Api.Helpers } } - private int? GetMaxBitrate(int? clientMaxBitrate, User user, string ipAddress) + private int? GetMaxBitrate(int? clientMaxBitrate, User user, IPAddress ipAddress) { var maxBitrate = clientMaxBitrate; var remoteClientMaxBitrate = user.RemoteClientBitrateLimit ?? 0; diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index 94856e03e..56585aeab 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Helpers authorization.Version, authorization.DeviceId, authorization.Device, - request.HttpContext.GetNormalizedRemoteIp(), + request.HttpContext.GetNormalizedRemoteIp().ToString(), user); if (session == null) diff --git a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs index 19fa95480..e51ad42d1 100644 --- a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs +++ b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Common.Extensions /// /// The HTTP context. /// The remote caller IP address. - public static string GetNormalizedRemoteIp(this HttpContext context) + public static IPAddress GetNormalizedRemoteIp(this HttpContext context) { // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests) var ip = context.Connection.RemoteIpAddress ?? IPAddress.Loopback; @@ -35,7 +35,7 @@ namespace MediaBrowser.Common.Extensions ip = ip.MapToIPv4(); } - return ip.ToString(); + return ip; } } } diff --git a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs index 09ffa8468..5b3d784ff 100644 --- a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; @@ -41,7 +42,7 @@ namespace Jellyfin.Api.Tests.Auth.LocalAccessPolicy public async Task LocalAccessOnly(bool isInLocalNetwork, bool shouldSucceed) { _networkManagerMock - .Setup(n => n.IsInLocalNetwork(It.IsAny())) + .Setup(n => n.IsInLocalNetwork(It.IsAny())) .Returns(isInLocalNetwork); TestHelpers.SetupConfigurationManager(_configurationManagerMock, true); From c2af50c51dd4d49ce44604ba5b34d77fad01a130 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 23 Mar 2021 17:39:55 +0100 Subject: [PATCH 170/402] Add tests for IsInNetwork --- .../NetworkManagerTests.cs | 63 +++++++++++++++++++ .../NetworkParseTests.cs | 28 +-------- 2 files changed, 64 insertions(+), 27 deletions(-) create mode 100644 tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs diff --git a/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs new file mode 100644 index 000000000..1cad625b7 --- /dev/null +++ b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs @@ -0,0 +1,63 @@ +using System.Net; +using Jellyfin.Networking.Configuration; +using Jellyfin.Networking.Manager; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; + +namespace Jellyfin.Networking.Tests +{ + public class NetworkManagerTests + { + /// + /// Checks that the given IP address is in the specified network(s). + /// + /// Network address(es). + /// The IP to check. + [Theory] + [InlineData("192.168.2.1/24", "192.168.2.123")] + [InlineData("192.168.2.1/24, !192.168.2.122/32", "192.168.2.123")] + [InlineData("fd23:184f:2029:0::/56", "fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0::/56, !fd23:184f:2029:0:3139:7386:67d7:d518/128", "fd23:184f:2029:0:3139:7386:67d7:d517")] + public void InNetwork_True_Success(string network, string value) + { + var ip = IPAddress.Parse(value); + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + LocalNetworkSubnets = network.Split(',') + }; + + using var networkManager = new NetworkManager(NetworkParseTests.GetMockConfig(conf), new NullLogger()); + + Assert.True(networkManager.IsInLocalNetwork(ip)); + } + + /// + /// Checks that thge given IP address is not in the network provided. + /// + /// Network address(es). + /// The IP to check. + [Theory] + [InlineData("192.168.10.0/24", "192.168.11.1")] + [InlineData("192.168.10.0/24, !192.168.10.60/32", "192.168.10.60")] + [InlineData("192.168.10.0/24", "fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0::/56", "fd24:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0::/56, !fd23:184f:2029:0:3139:7386:67d7:d500/120", "fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0::/56", "192.168.10.60")] + public void InNetwork_False_Success(string network, string value) + { + var ip = IPAddress.Parse(value); + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + LocalNetworkSubnets = network.Split(',') + }; + + using var nm = new NetworkManager(NetworkParseTests.GetMockConfig(conf), new NullLogger()); + + Assert.False(nm.IsInLocalNetwork(ip)); + } + } +} diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index c3469035e..d8541d76b 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Networking.Tests { public class NetworkParseTests { - private static IConfigurationManager GetMockConfig(NetworkConfiguration conf) + internal static IConfigurationManager GetMockConfig(NetworkConfiguration conf) { var configManager = new Mock { @@ -54,32 +54,6 @@ namespace Jellyfin.Networking.Tests Assert.Equal(nm.GetInternalBindAddresses().AsString(), value); } - /// - /// Check that the value given is in the network provided. - /// - /// Network address. - /// Value to check. - [Theory] - [InlineData("192.168.10.0/24, !192.168.10.60/32", "192.168.10.60")] - public void IsInNetwork(string network, string value) - { - if (network == null) - { - throw new ArgumentNullException(nameof(network)); - } - - var conf = new NetworkConfiguration() - { - EnableIPV6 = true, - EnableIPV4 = true, - LocalNetworkSubnets = network.Split(',') - }; - - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - - Assert.False(nm.IsInLocalNetwork(value)); - } - /// /// Checks IP address formats. /// From a4cac09d5b90dda5b7d7978e083676394022ffc1 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 23 Mar 2021 19:25:32 +0100 Subject: [PATCH 171/402] Use |= --- .../Manager/MetadataService.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 494e479a5..437b43eca 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -473,7 +473,7 @@ namespace MediaBrowser.Providers.Manager if ((originalPremiereDate ?? DateTime.MinValue) != (item.PremiereDate ?? DateTime.MinValue) || (originalProductionYear ?? -1) != (item.ProductionYear ?? -1)) { - updateType = updateType | ItemUpdateType.MetadataEdit; + updateType |= ItemUpdateType.MetadataEdit; } return updateType; @@ -493,7 +493,7 @@ namespace MediaBrowser.Providers.Manager if (currentList.Length != item.Genres.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) { - updateType = updateType | ItemUpdateType.MetadataEdit; + updateType |= ItemUpdateType.MetadataEdit; } } @@ -514,7 +514,7 @@ namespace MediaBrowser.Providers.Manager if (currentList.Length != item.Studios.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Studios.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) { - updateType = updateType | ItemUpdateType.MetadataEdit; + updateType |= ItemUpdateType.MetadataEdit; } } @@ -529,7 +529,7 @@ namespace MediaBrowser.Providers.Manager { if (item.UpdateRatingToItems(children)) { - updateType = updateType | ItemUpdateType.MetadataEdit; + updateType |= ItemUpdateType.MetadataEdit; } } @@ -686,7 +686,7 @@ namespace MediaBrowser.Providers.Manager var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType>(), cancellationToken) .ConfigureAwait(false); - refreshResult.UpdateType = refreshResult.UpdateType | remoteResult.UpdateType; + refreshResult.UpdateType |= remoteResult.UpdateType; refreshResult.ErrorMessage = remoteResult.ErrorMessage; refreshResult.Failures += remoteResult.Failures; } @@ -709,12 +709,12 @@ namespace MediaBrowser.Providers.Manager foreach (var remoteImage in localItem.RemoteImages) { await ProviderManager.SaveImage(item, remoteImage.url, remoteImage.type, null, cancellationToken).ConfigureAwait(false); - refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate; + refreshResult.UpdateType |= ItemUpdateType.ImageUpdate; } if (imageService.MergeImages(item, localItem.Images)) { - refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate; + refreshResult.UpdateType |= ItemUpdateType.ImageUpdate; } if (localItem.UserDataList != null) @@ -723,7 +723,7 @@ namespace MediaBrowser.Providers.Manager } MergeData(localItem, temp, Array.Empty(), !options.ReplaceAllMetadata, true); - refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; + refreshResult.UpdateType |= ItemUpdateType.MetadataImport; // Only one local provider allowed per item if (item.IsLocked || localItem.Item.IsLocked || IsFullLocalMetadata(localItem.Item)) @@ -755,7 +755,7 @@ namespace MediaBrowser.Providers.Manager var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType>(), cancellationToken) .ConfigureAwait(false); - refreshResult.UpdateType = refreshResult.UpdateType | remoteResult.UpdateType; + refreshResult.UpdateType |= remoteResult.UpdateType; refreshResult.ErrorMessage = remoteResult.ErrorMessage; refreshResult.Failures += remoteResult.Failures; } @@ -851,7 +851,7 @@ namespace MediaBrowser.Providers.Manager MergeData(result, temp, Array.Empty(), false, false); MergeNewData(temp.Item, id); - refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload; + refreshResult.UpdateType |= ItemUpdateType.MetadataDownload; } else { From d77507ba09a010dc653814ea620aafa42348a373 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 24 Mar 2021 18:09:40 +0100 Subject: [PATCH 172/402] Rewrite PasswordHash.Parse to work with ReadOnlySpans --- .../Cryptography/PasswordHash.cs | 121 +++++++++++---- .../Cryptography/PasswordHashTests.cs | 139 ++++++++++++++++++ .../PasswordHashTests.cs | 31 ---- 3 files changed, 233 insertions(+), 58 deletions(-) create mode 100644 tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/PasswordHashTests.cs diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs index 3e2eae1c8..81e980bf2 100644 --- a/MediaBrowser.Common/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#nullable enable using System; using System.Collections.Generic; @@ -30,6 +31,16 @@ namespace MediaBrowser.Common.Cryptography public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary parameters) { + if (id == null) + { + throw new ArgumentNullException(nameof(id)); + } + + if (id.Length == 0) + { + throw new ArgumentException("String can't be empty", nameof(id)); + } + Id = id; _hash = hash; _salt = salt; @@ -59,58 +70,109 @@ namespace MediaBrowser.Common.Cryptography /// Return the hashed password. public ReadOnlySpan Hash => _hash; - public static PasswordHash Parse(string hashString) + public static PasswordHash Parse(ReadOnlySpan hashString) { - // The string should at least contain the hash function and the hash itself - string[] splitted = hashString.Split('$'); - if (splitted.Length < 3) + if (hashString.IsEmpty) { - throw new ArgumentException("String doesn't contain enough segments", nameof(hashString)); + throw new ArgumentException("String can't be empty", nameof(hashString)); } - // Start at 1, the first index shouldn't contain any data - int index = 1; + if (hashString[0] != '$') + { + throw new FormatException("Hash string must start with a $"); + } - // Name of the hash function - string id = splitted[index++]; + // Ignore first $ + hashString = hashString[1..]; + + int nextSegment = hashString.IndexOf('$'); + if (hashString.IsEmpty || nextSegment == 0) + { + throw new FormatException("Hash string must contain a valid id"); + } + else if (nextSegment == -1) + { + return new PasswordHash(hashString.ToString(), Array.Empty()); + } + + ReadOnlySpan id = hashString[..nextSegment]; + hashString = hashString[(nextSegment + 1)..]; + Dictionary? parameters = null; + + nextSegment = hashString.IndexOf('$'); // Optional parameters - Dictionary parameters = new Dictionary(); - if (splitted[index].IndexOf('=', StringComparison.Ordinal) != -1) + ReadOnlySpan parametersSpan = nextSegment == -1 ? hashString : hashString[..nextSegment]; + if (parametersSpan.Contains('=')) { - foreach (string paramset in splitted[index++].Split(',')) + while (!parametersSpan.IsEmpty) { - if (string.IsNullOrEmpty(paramset)) + ReadOnlySpan parameter; + int index = parametersSpan.IndexOf(','); + if (index == -1) { - continue; + parameter = parametersSpan; + parametersSpan = ReadOnlySpan.Empty; + } + else + { + parameter = parametersSpan[..index]; + parametersSpan = parametersSpan[(index + 1)..]; } - string[] fields = paramset.Split('='); - if (fields.Length != 2) + int splitIndex = parameter.IndexOf('='); + if (splitIndex == -1 || splitIndex == 0 || splitIndex == parameter.Length - 1) { - throw new InvalidDataException($"Malformed parameter in password hash string {paramset}"); + throw new FormatException($"Malformed parameter in password hash string"); } - parameters.Add(fields[0], fields[1]); + (parameters ??= new Dictionary()).Add( + parameter[..splitIndex].ToString(), + parameter[(splitIndex + 1)..].ToString()); } + + if (nextSegment == -1) + { + // parameters can't be null here + return new PasswordHash(id.ToString(), Array.Empty(), Array.Empty(), parameters!); + } + + hashString = hashString[(nextSegment + 1)..]; + nextSegment = hashString.IndexOf('$'); + } + + if (nextSegment == 0) + { + throw new FormatException($"Hash string contains an empty segment"); } byte[] hash; byte[] salt; - // Check if the string also contains a salt - if (splitted.Length - index == 2) + if (nextSegment == -1) { - salt = Convert.FromHexString(splitted[index++]); - hash = Convert.FromHexString(splitted[index++]); + salt = Array.Empty(); + hash = Convert.FromHexString(hashString); } else { - salt = Array.Empty(); - hash = Convert.FromHexString(splitted[index++]); + salt = Convert.FromHexString(hashString[..nextSegment]); + hashString = hashString[(nextSegment + 1)..]; + nextSegment = hashString.IndexOf('$'); + if (nextSegment != -1) + { + throw new FormatException("Hash string contains too many segments"); + } + + if (hashString.IsEmpty) + { + throw new FormatException("Hash segment is empty"); + } + + hash = Convert.FromHexString(hashString); } - return new PasswordHash(id, hash, salt, parameters); + return new PasswordHash(id.ToString(), hash, salt, parameters ?? new Dictionary()); } private void SerializeParameters(StringBuilder stringBuilder) @@ -147,8 +209,13 @@ namespace MediaBrowser.Common.Cryptography .Append(Convert.ToHexString(_salt)); } - return str.Append('$') - .Append(Convert.ToHexString(_hash)).ToString(); + if (_hash.Length != 0) + { + str.Append('$') + .Append(Convert.ToHexString(_hash)); + } + + return str.ToString(); } } } diff --git a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs new file mode 100644 index 000000000..6da0a3037 --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Cryptography; +using Xunit; + +namespace Jellyfin.Common.Tests.Cryptography +{ + public static class PasswordHashTests + { + [Fact] + public static void Ctor_Null_ThrowsArgumentNullException() + { + Assert.Throws(() => new PasswordHash(null!, Array.Empty())); + } + + [Fact] + public static void Ctor_Empty_ThrowsArgumentException() + { + Assert.Throws(() => new PasswordHash(string.Empty, Array.Empty())); + } + + public static IEnumerable Parse_Valid_TestData() + { + yield return new object[] + { + "$PBKDF2", + new PasswordHash("PBKDF2", Array.Empty()) + }; + + yield return new object[] + { + "$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", + new PasswordHash( + "PBKDF2", + Convert.FromHexString("62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"), + Array.Empty(), + new Dictionary() + { + { "iterations", "1000" } + }) + }; + + yield return new object[] + { + "$PBKDF2$iterations=1000,m=120$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", + new PasswordHash( + "PBKDF2", + Convert.FromHexString("62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"), + Array.Empty(), + new Dictionary() + { + { "iterations", "1000" }, + { "m", "120" } + }) + }; + + yield return new object[] + { + "$PBKDF2$iterations=1000,m=120$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", + new PasswordHash( + "PBKDF2", + Convert.FromHexString("62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"), + Convert.FromHexString("69F420"), + new Dictionary() + { + { "iterations", "1000" }, + { "m", "120" } + }) + }; + + yield return new object[] + { + "$PBKDF2$iterations=1000,m=120", + new PasswordHash( + "PBKDF2", + Array.Empty(), + Array.Empty(), + new Dictionary() + { + { "iterations", "1000" }, + { "m", "120" } + }) + }; + } + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse_Valid_Success(string passwordHashString, PasswordHash expected) + { + var passwordHash = PasswordHash.Parse(passwordHashString); + Assert.Equal(expected.Id, passwordHash.Id); + Assert.Equal(expected.Parameters, passwordHash.Parameters); + Assert.Equal(expected.Salt.ToArray(), passwordHash.Salt.ToArray()); + Assert.Equal(expected.Hash.ToArray(), passwordHash.Hash.ToArray()); + Assert.Equal(expected.ToString(), passwordHash.ToString()); + } + + [Theory] + [InlineData("$PBKDF2")] + [InlineData("$PBKDF2$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] + [InlineData("$PBKDF2$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] + [InlineData("$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] + [InlineData("$PBKDF2$iterations=1000,m=120$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] + [InlineData("$PBKDF2$iterations=1000,m=120")] + public static void ToString_Roundtrip_Success(string passwordHash) + { + Assert.Equal(passwordHash, PasswordHash.Parse(passwordHash).ToString()); + } + + [Fact] + public static void Parse_Null_ThrowsArgumentException() + { + Assert.Throws(() => PasswordHash.Parse(null)); + } + + [Fact] + public static void Parse_Empty_ThrowsArgumentException() + { + Assert.Throws(() => PasswordHash.Parse(string.Empty)); + } + + [Theory] + [InlineData("$")] // No id + [InlineData("$$")] // Empty segments + [InlineData("PBKDF2$")] // Doesn't start with $ + [InlineData("$PBKDF2$$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Empty segment + [InlineData("$PBKDF2$iterations=1000$$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Empty salt segment + [InlineData("$PBKDF2$=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter + [InlineData("$PBKDF2$=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter + [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter + [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $ + [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment + [InlineData("$PBKDF2$69F420$")] // Empty hash + public static void Parse_InvalidFormat_ThrowsFormatException(string passwordHash) + { + Assert.Throws(() => PasswordHash.Parse(passwordHash)); + } + } +} diff --git a/tests/Jellyfin.Common.Tests/PasswordHashTests.cs b/tests/Jellyfin.Common.Tests/PasswordHashTests.cs deleted file mode 100644 index c4422bd10..000000000 --- a/tests/Jellyfin.Common.Tests/PasswordHashTests.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using MediaBrowser.Common; -using MediaBrowser.Common.Cryptography; -using Xunit; - -namespace Jellyfin.Common.Tests -{ - public class PasswordHashTests - { - [Theory] - [InlineData( - "$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", - "PBKDF2", - "", - "62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] - public void ParseTest(string passwordHash, string id, string salt, string hash) - { - var pass = PasswordHash.Parse(passwordHash); - Assert.Equal(id, pass.Id); - Assert.Equal(salt, Convert.ToHexString(pass.Salt)); - Assert.Equal(hash, Convert.ToHexString(pass.Hash)); - } - - [Theory] - [InlineData("$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] - public void ToStringTest(string passwordHash) - { - Assert.Equal(passwordHash, PasswordHash.Parse(passwordHash).ToString()); - } - } -} From c2cd7fa0b2441212ac0de16ffcef18883ee8c665 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 24 Mar 2021 18:39:33 +0100 Subject: [PATCH 173/402] Add more PasswordHash tests --- .../Cryptography/PasswordHashTests.cs | 76 +++++++++++++++---- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs index 6da0a3037..e6c325bac 100644 --- a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs +++ b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs @@ -21,12 +21,65 @@ namespace Jellyfin.Common.Tests.Cryptography public static IEnumerable Parse_Valid_TestData() { + // Id yield return new object[] { "$PBKDF2", new PasswordHash("PBKDF2", Array.Empty()) }; + // Id + parameter + yield return new object[] + { + "$PBKDF2$iterations=1000", + new PasswordHash( + "PBKDF2", + Array.Empty(), + Array.Empty(), + new Dictionary() + { + { "iterations", "1000" }, + }) + }; + + // Id + parameters + yield return new object[] + { + "$PBKDF2$iterations=1000,m=120", + new PasswordHash( + "PBKDF2", + Array.Empty(), + Array.Empty(), + new Dictionary() + { + { "iterations", "1000" }, + { "m", "120" } + }) + }; + + // Id + hash + yield return new object[] + { + "$PBKDF2$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", + new PasswordHash( + "PBKDF2", + Convert.FromHexString("62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"), + Array.Empty(), + new Dictionary()) + }; + + // Id + salt + hash + yield return new object[] + { + "$PBKDF2$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", + new PasswordHash( + "PBKDF2", + Convert.FromHexString("62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"), + Convert.FromHexString("69F420"), + new Dictionary()) + }; + + // Id + parameter + hash yield return new object[] { "$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", @@ -40,6 +93,7 @@ namespace Jellyfin.Common.Tests.Cryptography }) }; + // Id + parameters + hash yield return new object[] { "$PBKDF2$iterations=1000,m=120$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", @@ -54,6 +108,7 @@ namespace Jellyfin.Common.Tests.Cryptography }) }; + // Id + parameters + salt + hash yield return new object[] { "$PBKDF2$iterations=1000,m=120$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D", @@ -67,20 +122,6 @@ namespace Jellyfin.Common.Tests.Cryptography { "m", "120" } }) }; - - yield return new object[] - { - "$PBKDF2$iterations=1000,m=120", - new PasswordHash( - "PBKDF2", - Array.Empty(), - Array.Empty(), - new Dictionary() - { - { "iterations", "1000" }, - { "m", "120" } - }) - }; } [Theory] @@ -101,6 +142,7 @@ namespace Jellyfin.Common.Tests.Cryptography [InlineData("$PBKDF2$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] [InlineData("$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] [InlineData("$PBKDF2$iterations=1000,m=120$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] + [InlineData("$PBKDF2$iterations=1000,m=120$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] [InlineData("$PBKDF2$iterations=1000,m=120")] public static void ToString_Roundtrip_Success(string passwordHash) { @@ -125,11 +167,15 @@ namespace Jellyfin.Common.Tests.Cryptography [InlineData("PBKDF2$")] // Doesn't start with $ [InlineData("$PBKDF2$$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Empty segment [InlineData("$PBKDF2$iterations=1000$$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Empty salt segment + [InlineData("$PBKDF2$iterations=1000$69F420$")] // Empty hash segment [InlineData("$PBKDF2$=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter [InlineData("$PBKDF2$=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $ - [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment + [InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment + [InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment + [InlineData("$PBKDF2$iterations=$invalidstalt$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid salt + [InlineData("$PBKDF2$iterations=$69F420$invalid hash")] // Invalid hash [InlineData("$PBKDF2$69F420$")] // Empty hash public static void Parse_InvalidFormat_ThrowsFormatException(string passwordHash) { From 136136dea95321a1b691d3d5cafeac4f946aaf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Aceda=C5=84ski?= Date: Wed, 24 Mar 2021 20:26:01 +0100 Subject: [PATCH 174/402] Fix incorrect responses for HEAD /audio//stream Without this fix my Samsung Soundbar (HW-Q80R) fails to play using DLNA and returns "Error: Resource not found (716)" instead. I had a look on tcpdump network logs between Jellyfin and the soundbar and noticed that the device performs a HEAD request for the media before responding to the DLNA UPNP control request from Jellyfin (or BubbleUPNP Android App). Jellyfin retuns 204 No Content response, which is unusual. Common web servers generally return 200 OK if the GET would return content, and this is not-very-clearly suggested [in HTTP spec](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) The other patch is to ensure, that invalid Content-Length: 0 is not returned with the HEAD response in the streaming case. I think in both cases we still don't return the same headers with HEAD as with GET (e.g. Content-Length or Accept-Ranges), but at least we don't return anything misleading. --- Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index f828b1d9d..a567fce1e 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -46,7 +46,8 @@ namespace Jellyfin.Api.Helpers if (isHeadRequest) { - return new FileContentResult(Array.Empty(), contentType); + httpContext.Response.Headers[HeaderNames.ContentType] = contentType; + return new OkResult(); } return new FileStreamResult(await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false), contentType); @@ -71,7 +72,7 @@ namespace Jellyfin.Api.Helpers // if the request is a head request, return a NoContent result with the same headers as it would with a GET request if (isHeadRequest) { - return new NoContentResult(); + return new OkResult(); } return new PhysicalFileResult(path, contentType) { EnableRangeProcessing = true }; From 066c19a26b1dbc36c20439426110738b2ce1c2d6 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 24 Mar 2021 21:06:03 +0100 Subject: [PATCH 175/402] Fix possible null ref exception --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 46a7feb7f..c18838caf 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -208,7 +208,7 @@ namespace Emby.Server.Implementations.Library /// Gets or sets the list of entity resolution ignore rules. /// /// The entity resolution ignore rules. - private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } + private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } = Array.Empty(); /// /// Gets or sets the list of currently registered entity resolvers. From 86852178c21f19944360ab0728042faa127d0128 Mon Sep 17 00:00:00 2001 From: cocool97 <34218602+cocool97@users.noreply.github.com> Date: Wed, 24 Mar 2021 21:23:59 +0100 Subject: [PATCH 176/402] Update MediaBrowser.Controller/LiveTv/ChannelInfo.cs Co-authored-by: Cody Robibero --- MediaBrowser.Controller/LiveTv/ChannelInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs index 0f3f87847..166c4d77c 100644 --- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs @@ -46,9 +46,9 @@ namespace MediaBrowser.Controller.LiveTv public ChannelType ChannelType { get; set; } /// - /// Gets or sets the group of the channel + /// Gets or sets the group of the channel. /// - /// The group of the channel + /// The group of the channel. public string ChannelGroup { get; set; } /// From b1e8a8565f316e9b3d65b3a681384dfc8d743b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Aceda=C5=84ski?= Date: Wed, 24 Mar 2021 22:46:08 +0100 Subject: [PATCH 177/402] Update Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs Co-authored-by: Claus Vium --- Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index a567fce1e..b0fd59e5e 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -69,7 +69,7 @@ namespace Jellyfin.Api.Helpers { httpContext.Response.ContentType = contentType; - // if the request is a head request, return a NoContent result with the same headers as it would with a GET request + // if the request is a head request, return an OkResult (200) with the same headers as it would with a GET request if (isHeadRequest) { return new OkResult(); From ef9eba8bc9f697ed6d8bf973d7f5c8d7865ecd18 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 25 Mar 2021 11:45:27 +0100 Subject: [PATCH 178/402] Ignore format for ISO files --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 6 ++++++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 09080e7b2..92b9a8c7e 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -313,6 +313,12 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } + // ISO files don't have an ffmpeg format + if (string.Equals(container, "iso", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + return container; } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 47cf020b4..205933ae2 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -370,7 +370,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public string GetInputArgument(string inputFile, MediaSourceInfo mediaSource) { var prefix = "file"; - if (mediaSource.VideoType == VideoType.BluRay || mediaSource.VideoType == VideoType.Iso) + if (mediaSource.VideoType == VideoType.BluRay) { prefix = "bluray"; } From 5bb7d99b48feb66a393da73f4a056b6dd5f38739 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 25 Mar 2021 13:16:09 +0100 Subject: [PATCH 179/402] Remove DVDs from files exempt from chapter image extraction --- Emby.Server.Implementations/MediaEncoder/EncodingManager.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index c6e931448..a9dab9138 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -82,11 +82,6 @@ namespace Emby.Server.Implementations.MediaEncoder return false; } - if (video.VideoType == VideoType.Dvd) - { - return false; - } - if (video.IsShortcut) { return false; From b3d084044e2d98590d9c939090ba522aa66dff82 Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 25 Mar 2021 15:09:37 +0100 Subject: [PATCH 180/402] enable range processing for download endpoints --- Jellyfin.Api/Controllers/LibraryController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index f8e8825ef..1d4bbe61e 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -114,7 +114,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - return PhysicalFile(item.Path, MimeTypes.GetMimeType(item.Path)); + return PhysicalFile(item.Path, MimeTypes.GetMimeType(item.Path), true); } /// @@ -666,7 +666,7 @@ namespace Jellyfin.Api.Controllers } // TODO determine non-ASCII validity. - return PhysicalFile(path, MimeTypes.GetMimeType(path), filename); + return PhysicalFile(path, MimeTypes.GetMimeType(path), filename, true); } /// From 4cea6d9ccfa5bddaef27800aac1c125ae22747d7 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 25 Mar 2021 18:53:36 +0100 Subject: [PATCH 181/402] Apply suggestions from code review Co-authored-by: Cody Robibero --- MediaBrowser.Common/Cryptography/PasswordHash.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs index 81e980bf2..f2ecc4741 100644 --- a/MediaBrowser.Common/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs @@ -123,7 +123,7 @@ namespace MediaBrowser.Common.Cryptography int splitIndex = parameter.IndexOf('='); if (splitIndex == -1 || splitIndex == 0 || splitIndex == parameter.Length - 1) { - throw new FormatException($"Malformed parameter in password hash string"); + throw new FormatException("Malformed parameter in password hash string"); } (parameters ??= new Dictionary()).Add( @@ -143,7 +143,7 @@ namespace MediaBrowser.Common.Cryptography if (nextSegment == 0) { - throw new FormatException($"Hash string contains an empty segment"); + throw new FormatException("Hash string contains an empty segment"); } byte[] hash; From 36669ff45146dd8d4dc063b6a6b1d057f2d33464 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 17 Mar 2021 19:08:11 -0400 Subject: [PATCH 182/402] Use correct setter access modifiers --- Jellyfin.Data/Entities/AccessSchedule.cs | 8 ++-- Jellyfin.Data/Entities/ActivityLog.cs | 7 ++- .../Entities/CustomItemDisplayPreferences.cs | 4 +- Jellyfin.Data/Entities/DisplayPreferences.cs | 12 +++-- Jellyfin.Data/Entities/Group.cs | 16 +++---- Jellyfin.Data/Entities/HomeSection.cs | 7 ++- Jellyfin.Data/Entities/ImageInfo.cs | 8 ++-- .../Entities/ItemDisplayPreferences.cs | 4 +- Jellyfin.Data/Entities/Libraries/Artwork.cs | 8 ++-- Jellyfin.Data/Entities/Libraries/Book.cs | 8 ++-- .../Entities/Libraries/BookMetadata.cs | 6 +-- Jellyfin.Data/Entities/Libraries/Chapter.cs | 8 ++-- .../Entities/Libraries/Collection.cs | 11 +++-- .../Entities/Libraries/CollectionItem.cs | 2 +- Jellyfin.Data/Entities/Libraries/Company.cs | 16 +++---- .../Entities/Libraries/CustomItem.cs | 8 ++-- Jellyfin.Data/Entities/Libraries/Episode.cs | 8 ++-- Jellyfin.Data/Entities/Libraries/Genre.cs | 6 +-- .../Entities/Libraries/ItemMetadata.cs | 37 +++++++--------- Jellyfin.Data/Entities/Libraries/Library.cs | 6 +-- .../Entities/Libraries/LibraryItem.cs | 10 ++--- Jellyfin.Data/Entities/Libraries/MediaFile.cs | 12 +++-- .../Entities/Libraries/MediaFileStream.cs | 6 +-- .../Entities/Libraries/MetadataProvider.cs | 6 +-- .../Entities/Libraries/MetadataProviderId.cs | 6 +-- Jellyfin.Data/Entities/Libraries/Movie.cs | 8 ++-- .../Entities/Libraries/MovieMetadata.cs | 6 +-- .../Entities/Libraries/MusicAlbum.cs | 10 ++--- .../Entities/Libraries/MusicAlbumMetadata.cs | 6 +-- Jellyfin.Data/Entities/Libraries/Person.cs | 16 +++---- .../Entities/Libraries/PersonRole.cs | 14 +++--- Jellyfin.Data/Entities/Libraries/Photo.cs | 8 ++-- Jellyfin.Data/Entities/Libraries/Rating.cs | 6 +-- .../Entities/Libraries/RatingSource.cs | 6 +-- Jellyfin.Data/Entities/Libraries/Release.cs | 16 +++---- Jellyfin.Data/Entities/Libraries/Season.cs | 10 ++--- Jellyfin.Data/Entities/Libraries/Series.cs | 11 ++--- .../Entities/Libraries/SeriesMetadata.cs | 6 +-- Jellyfin.Data/Entities/Libraries/Track.cs | 8 ++-- Jellyfin.Data/Entities/Permission.cs | 10 ++--- Jellyfin.Data/Entities/Preference.cs | 10 ++--- Jellyfin.Data/Entities/User.cs | 44 +++++++------------ 42 files changed, 179 insertions(+), 246 deletions(-) diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs index 7974d3add..befc4ca02 100644 --- a/Jellyfin.Data/Entities/AccessSchedule.cs +++ b/Jellyfin.Data/Entities/AccessSchedule.cs @@ -26,20 +26,20 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the id of this instance. + /// Gets the id of this instance. /// /// /// Identity, Indexed, Required. /// [XmlIgnore] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// - /// Gets or sets the id of the associated user. + /// Gets the id of the associated user. /// [XmlIgnore] - public Guid UserId { get; protected set; } + public Guid UserId { get; private set; } /// /// Gets or sets the day of week. diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index e4534e8b5..1d1b86552 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -38,11 +38,10 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the identity of this instance. - /// This is the key in the backing database. + /// Gets the identity of this instance. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -120,7 +119,7 @@ namespace Jellyfin.Data.Entities /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs index cc46248c7..f12737f65 100644 --- a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs @@ -27,13 +27,13 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the Id. + /// Gets the Id. /// /// /// Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the user Id. diff --git a/Jellyfin.Data/Entities/DisplayPreferences.cs b/Jellyfin.Data/Entities/DisplayPreferences.cs index 64cd6812a..646961238 100644 --- a/Jellyfin.Data/Entities/DisplayPreferences.cs +++ b/Jellyfin.Data/Entities/DisplayPreferences.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA2227 - -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -35,13 +33,13 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the Id. + /// Gets the Id. /// /// /// Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the user Id. @@ -145,8 +143,8 @@ namespace Jellyfin.Data.Entities public string? TvHome { get; set; } /// - /// Gets or sets the home sections. + /// Gets the home sections. /// - public virtual ICollection HomeSections { get; protected set; } + public virtual ICollection HomeSections { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index b14e22b7b..14da0bb15 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -33,12 +31,12 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the id of this group. + /// Gets the id of this group. /// /// /// Identity, Indexed, Required. /// - public Guid Id { get; protected set; } + public Guid Id { get; private set; } /// /// Gets or sets the group's name. @@ -52,17 +50,17 @@ namespace Jellyfin.Data.Entities /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets a collection containing the group's permissions. + /// Gets a collection containing the group's permissions. /// - public virtual ICollection Permissions { get; protected set; } + public virtual ICollection Permissions { get; private set; } /// - /// Gets or sets a collection containing the group's preferences. + /// Gets a collection containing the group's preferences. /// - public virtual ICollection Preferences { get; protected set; } + public virtual ICollection Preferences { get; private set; } /// public bool HasPermission(PermissionKind kind) diff --git a/Jellyfin.Data/Entities/HomeSection.cs b/Jellyfin.Data/Entities/HomeSection.cs index 5adc52491..e194aa537 100644 --- a/Jellyfin.Data/Entities/HomeSection.cs +++ b/Jellyfin.Data/Entities/HomeSection.cs @@ -1,5 +1,4 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; namespace Jellyfin.Data.Entities @@ -10,13 +9,13 @@ namespace Jellyfin.Data.Entities public class HomeSection { /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity. Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the Id of the associated display preferences. diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index e0c37047d..b5c7a1c12 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -20,18 +20,18 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// - /// Gets or sets the user id. + /// Gets the user id. /// - public Guid? UserId { get; protected set; } + public Guid? UserId { get; private set; } /// /// Gets or sets the path of the image. diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs index 4bfeb2fa3..948126d0a 100644 --- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs @@ -29,13 +29,13 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the Id. + /// Gets the id. /// /// /// Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the user Id. diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs index 84a524de2..923525fc5 100644 --- a/Jellyfin.Data/Entities/Libraries/Artwork.cs +++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -30,13 +28,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the path. @@ -58,7 +56,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Book.cs b/Jellyfin.Data/Entities/Libraries/Book.cs index aea3d58d5..a838686d0 100644 --- a/Jellyfin.Data/Entities/Libraries/Book.cs +++ b/Jellyfin.Data/Entities/Libraries/Book.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets a collection containing the metadata for this book. + /// Gets a collection containing the metadata for this book. /// - public virtual ICollection BookMetadata { get; protected set; } + public virtual ICollection BookMetadata { get; private set; } /// - public virtual ICollection Releases { get; protected set; } + public virtual ICollection Releases { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs index 1ff4327b0..4a350d200 100644 --- a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -26,9 +24,9 @@ namespace Jellyfin.Data.Entities.Libraries public long? Isbn { get; set; } /// - /// Gets or sets a collection of the publishers for this book. + /// Gets a collection of the publishers for this book. /// - public virtual ICollection Publishers { get; protected set; } + public virtual ICollection Publishers { get; private set; } /// public ICollection Companies => Publishers; diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs index 11f53ae20..3d81f713d 100644 --- a/Jellyfin.Data/Entities/Libraries/Chapter.cs +++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -29,13 +27,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -74,7 +72,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; protected set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs index 4253b7ecc..7de601969 100644 --- a/Jellyfin.Data/Entities/Libraries/Collection.cs +++ b/Jellyfin.Data/Entities/Libraries/Collection.cs @@ -1,5 +1,4 @@ #pragma warning disable CA1711 // Identifiers should not have incorrect suffix -#pragma warning disable CA2227 using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -22,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -42,12 +41,12 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets a collection containing this collection's items. + /// Gets a collection containing this collection's items. /// - public virtual ICollection Items { get; protected set; } + public virtual ICollection Items { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs index e19362bdf..0cb4716db 100644 --- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs @@ -29,7 +29,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// /// Gets or sets the library item. diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs index 09050bb52..1abbee445 100644 --- a/Jellyfin.Data/Entities/Libraries/Company.cs +++ b/Jellyfin.Data/Entities/Libraries/Company.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -22,27 +20,27 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets a collection containing the metadata. + /// Gets a collection containing the metadata. /// - public virtual ICollection CompanyMetadata { get; protected set; } + public virtual ICollection CompanyMetadata { get; private set; } /// - /// Gets or sets a collection containing this company's child companies. + /// Gets a collection containing this company's child companies. /// - public virtual ICollection ChildCompanies { get; protected set; } + public virtual ICollection ChildCompanies { get; private set; } /// public ICollection Companies => ChildCompanies; diff --git a/Jellyfin.Data/Entities/Libraries/CustomItem.cs b/Jellyfin.Data/Entities/Libraries/CustomItem.cs index 88d1a0c25..e27d01d86 100644 --- a/Jellyfin.Data/Entities/Libraries/CustomItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CustomItem.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets a collection containing the metadata for this item. + /// Gets a collection containing the metadata for this item. /// - public virtual ICollection CustomItemMetadata { get; protected set; } + public virtual ICollection CustomItemMetadata { get; private set; } /// - public virtual ICollection Releases { get; protected set; } + public virtual ICollection Releases { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Episode.cs b/Jellyfin.Data/Entities/Libraries/Episode.cs index 458c7d9f5..ce2f0c617 100644 --- a/Jellyfin.Data/Entities/Libraries/Episode.cs +++ b/Jellyfin.Data/Entities/Libraries/Episode.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -26,11 +24,11 @@ namespace Jellyfin.Data.Entities.Libraries public int? EpisodeNumber { get; set; } /// - public virtual ICollection Releases { get; protected set; } + public virtual ICollection Releases { get; private set; } /// - /// Gets or sets a collection containing the metadata for this episode. + /// Gets a collection containing the metadata for this episode. /// - public virtual ICollection EpisodeMetadata { get; protected set; } + public virtual ICollection EpisodeMetadata { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs index 9f3d65028..3b822ee82 100644 --- a/Jellyfin.Data/Entities/Libraries/Genre.cs +++ b/Jellyfin.Data/Entities/Libraries/Genre.cs @@ -19,13 +19,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -39,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; protected set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs index d12e011a8..d429a90c6 100644 --- a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -43,13 +41,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the title. @@ -99,12 +97,12 @@ namespace Jellyfin.Data.Entities.Libraries public DateTimeOffset? ReleaseDate { get; set; } /// - /// Gets or sets the date added. + /// Gets the date added. /// /// /// Required. /// - public DateTime DateAdded { get; protected set; } + public DateTime DateAdded { get; private set; } /// /// Gets or sets the date modified. @@ -114,37 +112,32 @@ namespace Jellyfin.Data.Entities.Libraries /// public DateTime DateModified { get; set; } - /// - /// Gets or sets the row version. - /// - /// - /// Required, ConcurrencyToken. - /// + /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets a collection containing the person roles for this item. + /// Gets a collection containing the person roles for this item. /// - public virtual ICollection PersonRoles { get; protected set; } + public virtual ICollection PersonRoles { get; private set; } /// - /// Gets or sets a collection containing the genres for this item. + /// Gets a collection containing the genres for this item. /// - public virtual ICollection Genres { get; protected set; } + public virtual ICollection Genres { get; private set; } /// - public virtual ICollection Artwork { get; protected set; } + public virtual ICollection Artwork { get; private set; } /// - /// Gets or sets a collection containing the ratings for this item. + /// Gets a collection containing the ratings for this item. /// - public virtual ICollection Ratings { get; protected set; } + public virtual ICollection Ratings { get; private set; } /// - /// Gets or sets a collection containing the metadata sources for this item. + /// Gets a collection containing the metadata sources for this item. /// - public virtual ICollection Sources { get; protected set; } + public virtual ICollection Sources { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Library.cs b/Jellyfin.Data/Entities/Libraries/Library.cs index e45384902..0db42a1c7 100644 --- a/Jellyfin.Data/Entities/Libraries/Library.cs +++ b/Jellyfin.Data/Entities/Libraries/Library.cs @@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -49,7 +49,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs index 67ffad944..d889b871e 100644 --- a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs +++ b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs @@ -21,22 +21,22 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// - /// Gets or sets the date this library item was added. + /// Gets the date this library item was added. /// - public DateTime DateAdded { get; protected set; } + public DateTime DateAdded { get; private set; } /// [ConcurrencyCheck] - public uint RowVersion { get; protected set; } + public uint RowVersion { get; private set; } /// /// Gets or sets the library of this item. diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs index f3e2fe653..36e1e4777 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFile.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -33,13 +31,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the path relative to the library root. @@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets a collection containing the streams in this file. + /// Gets a collection containing the streams in this file. /// - public virtual ICollection MediaFileStreams { get; protected set; } + public virtual ICollection MediaFileStreams { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs index ba21294fc..e24e73ecb 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFileStream.cs @@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the stream number. @@ -39,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs index fb2587882..b27196078 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs @@ -25,13 +25,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -45,7 +45,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs index 2a9c904c8..44c198518 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs @@ -27,13 +27,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the provider id. @@ -47,7 +47,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// /// Gets or sets the metadata provider. diff --git a/Jellyfin.Data/Entities/Libraries/Movie.cs b/Jellyfin.Data/Entities/Libraries/Movie.cs index f89cacff4..499fafd0e 100644 --- a/Jellyfin.Data/Entities/Libraries/Movie.cs +++ b/Jellyfin.Data/Entities/Libraries/Movie.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries } /// - public virtual ICollection Releases { get; protected set; } + public virtual ICollection Releases { get; private set; } /// - /// Gets or sets a collection containing the metadata for this movie. + /// Gets a collection containing the metadata for this movie. /// - public virtual ICollection MovieMetadata { get; protected set; } + public virtual ICollection MovieMetadata { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs index fb181dea6..44b5f34d7 100644 --- a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Jellyfin.Data.Interfaces; @@ -62,9 +60,9 @@ namespace Jellyfin.Data.Entities.Libraries public string? Country { get; set; } /// - /// Gets or sets the studios that produced this movie. + /// Gets the studios that produced this movie. /// - public virtual ICollection Studios { get; protected set; } + public virtual ICollection Studios { get; private set; } /// public ICollection Companies => Studios; diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs index 4049cdac8..d6231bbf0 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; namespace Jellyfin.Data.Entities.Libraries @@ -20,13 +18,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets a collection containing the album metadata. + /// Gets a collection containing the album metadata. /// - public virtual ICollection MusicAlbumMetadata { get; protected set; } + public virtual ICollection MusicAlbumMetadata { get; private set; } /// - /// Gets or sets a collection containing the tracks. + /// Gets a collection containing the tracks. /// - public virtual ICollection Tracks { get; protected set; } + public virtual ICollection Tracks { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs index 3080bd692..691f3504f 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -51,8 +49,8 @@ namespace Jellyfin.Data.Entities.Libraries public string? Country { get; set; } /// - /// Gets or sets a collection containing the labels. + /// Gets a collection containing the labels. /// - public virtual ICollection Labels { get; protected set; } + public virtual ICollection Labels { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs index 159bd47be..8b67d920d 100644 --- a/Jellyfin.Data/Entities/Libraries/Person.cs +++ b/Jellyfin.Data/Entities/Libraries/Person.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -32,13 +30,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities.Libraries public string? SourceId { get; set; } /// - /// Gets or sets the date added. + /// Gets the date added. /// /// /// Required. /// - public DateTime DateAdded { get; protected set; } + public DateTime DateAdded { get; private set; } /// /// Gets or sets the date modified. @@ -78,12 +76,12 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets a list of metadata sources for this person. + /// Gets a list of metadata sources for this person. /// - public virtual ICollection Sources { get; protected set; } + public virtual ICollection Sources { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs index 988aa84ba..7d40bdf44 100644 --- a/Jellyfin.Data/Entities/Libraries/PersonRole.cs +++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -27,13 +25,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name of the person's role. @@ -55,7 +53,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; protected set; } + public uint RowVersion { get; private set; } /// /// Gets or sets the person. @@ -66,12 +64,12 @@ namespace Jellyfin.Data.Entities.Libraries public virtual Person Person { get; set; } /// - public virtual ICollection Artwork { get; protected set; } + public virtual ICollection Artwork { get; private set; } /// - /// Gets or sets a collection containing the metadata sources for this person role. + /// Gets a collection containing the metadata sources for this person role. /// - public virtual ICollection Sources { get; protected set; } + public virtual ICollection Sources { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Photo.cs b/Jellyfin.Data/Entities/Libraries/Photo.cs index eb5c96267..4b459432b 100644 --- a/Jellyfin.Data/Entities/Libraries/Photo.cs +++ b/Jellyfin.Data/Entities/Libraries/Photo.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -21,11 +19,11 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets a collection containing the photo metadata. + /// Gets a collection containing the photo metadata. /// - public virtual ICollection PhotoMetadata { get; protected set; } + public virtual ICollection PhotoMetadata { get; private set; } /// - public virtual ICollection Releases { get; protected set; } + public virtual ICollection Releases { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs index 6862012a8..58c8fa49e 100644 --- a/Jellyfin.Data/Entities/Libraries/Rating.cs +++ b/Jellyfin.Data/Entities/Libraries/Rating.cs @@ -19,13 +19,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the value. @@ -42,7 +42,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// /// Gets or sets the rating type. diff --git a/Jellyfin.Data/Entities/Libraries/RatingSource.cs b/Jellyfin.Data/Entities/Libraries/RatingSource.cs index ae0d806ff..0f3a07324 100644 --- a/Jellyfin.Data/Entities/Libraries/RatingSource.cs +++ b/Jellyfin.Data/Entities/Libraries/RatingSource.cs @@ -21,13 +21,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -57,7 +57,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// /// Gets or sets the metadata source. diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs index 21d403979..d3d52bf5c 100644 --- a/Jellyfin.Data/Entities/Libraries/Release.cs +++ b/Jellyfin.Data/Entities/Libraries/Release.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -31,13 +29,13 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Gets or sets the id. + /// Gets the id. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// /// Gets or sets the name. @@ -51,17 +49,17 @@ namespace Jellyfin.Data.Entities.Libraries /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets a collection containing the media files for this release. + /// Gets a collection containing the media files for this release. /// - public virtual ICollection MediaFiles { get; protected set; } + public virtual ICollection MediaFiles { get; private set; } /// - /// Gets or sets a collection containing the chapters for this release. + /// Gets a collection containing the chapters for this release. /// - public virtual ICollection Chapters { get; protected set; } + public virtual ICollection Chapters { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Season.cs b/Jellyfin.Data/Entities/Libraries/Season.cs index 04f723a1d..fc110b49d 100644 --- a/Jellyfin.Data/Entities/Libraries/Season.cs +++ b/Jellyfin.Data/Entities/Libraries/Season.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; namespace Jellyfin.Data.Entities.Libraries @@ -25,13 +23,13 @@ namespace Jellyfin.Data.Entities.Libraries public int? SeasonNumber { get; set; } /// - /// Gets or sets the season metadata. + /// Gets the season metadata. /// - public virtual ICollection SeasonMetadata { get; protected set; } + public virtual ICollection SeasonMetadata { get; private set; } /// - /// Gets or sets a collection containing the number of episodes. + /// Gets a collection containing the number of episodes. /// - public virtual ICollection Episodes { get; protected set; } + public virtual ICollection Episodes { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Series.cs b/Jellyfin.Data/Entities/Libraries/Series.cs index 59508831e..0354433e0 100644 --- a/Jellyfin.Data/Entities/Libraries/Series.cs +++ b/Jellyfin.Data/Entities/Libraries/Series.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.Collections.Generic; @@ -16,7 +14,6 @@ namespace Jellyfin.Data.Entities.Libraries /// The library. public Series(Library library) : base(library) { - DateAdded = DateTime.UtcNow; Seasons = new HashSet(); SeriesMetadata = new HashSet(); } @@ -37,13 +34,13 @@ namespace Jellyfin.Data.Entities.Libraries public DateTime? FirstAired { get; set; } /// - /// Gets or sets a collection containing the series metadata. + /// Gets a collection containing the series metadata. /// - public virtual ICollection SeriesMetadata { get; protected set; } + public virtual ICollection SeriesMetadata { get; private set; } /// - /// Gets or sets a collection containing the seasons. + /// Gets a collection containing the seasons. /// - public virtual ICollection Seasons { get; protected set; } + public virtual ICollection Seasons { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs index 730deccae..b75b0a25b 100644 --- a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -63,9 +61,9 @@ namespace Jellyfin.Data.Entities.Libraries public string? Country { get; set; } /// - /// Gets or sets a collection containing the networks. + /// Gets a collection containing the networks. /// - public virtual ICollection Networks { get; protected set; } + public virtual ICollection Networks { get; private set; } /// public ICollection Companies => Networks; diff --git a/Jellyfin.Data/Entities/Libraries/Track.cs b/Jellyfin.Data/Entities/Libraries/Track.cs index 86a3edff8..d35400033 100644 --- a/Jellyfin.Data/Entities/Libraries/Track.cs +++ b/Jellyfin.Data/Entities/Libraries/Track.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System.Collections.Generic; using Jellyfin.Data.Interfaces; @@ -26,11 +24,11 @@ namespace Jellyfin.Data.Entities.Libraries public int? TrackNumber { get; set; } /// - public virtual ICollection Releases { get; protected set; } + public virtual ICollection Releases { get; private set; } /// - /// Gets or sets a collection containing the track metadata. + /// Gets a collection containing the track metadata. /// - public virtual ICollection TrackMetadata { get; protected set; } + public virtual ICollection TrackMetadata { get; private set; } } } diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index f059dedfa..005d1a10f 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -25,21 +25,21 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the id of this permission. + /// Gets the id of this permission. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// - /// Gets or sets the type of this permission. + /// Gets the type of this permission. /// /// /// Required. /// - public PermissionKind Kind { get; protected set; } + public PermissionKind Kind { get; private set; } /// /// Gets or sets a value indicating whether the associated user has this permission. @@ -51,7 +51,7 @@ namespace Jellyfin.Data.Entities /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index a8813ab88..048caecee 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -24,21 +24,21 @@ namespace Jellyfin.Data.Entities } /// - /// Gets or sets the id of this preference. + /// Gets the id of this preference. /// /// /// Identity, Indexed, Required. /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } + public int Id { get; private set; } /// - /// Gets or sets the type of this preference. + /// Gets the type of this preference. /// /// /// Required. /// - public PreferenceKind Kind { get; protected set; } + public PreferenceKind Kind { get; private set; } /// /// Gets or sets the value of this preference. @@ -52,7 +52,7 @@ namespace Jellyfin.Data.Entities /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 74331726c..e309e54de 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA2227 - using System; using System.Collections.Generic; using System.ComponentModel; @@ -302,64 +300,54 @@ namespace Jellyfin.Data.Entities public virtual ImageInfo? ProfileImage { get; set; } /// - /// Gets or sets the user's display preferences. + /// Gets the user's display preferences. /// - /// - /// Required. - /// - public virtual ICollection DisplayPreferences { get; set; } + public virtual ICollection DisplayPreferences { get; private set; } /// /// Gets or sets the level of sync play permissions this user has. /// public SyncPlayUserAccessType SyncPlayAccess { get; set; } - /// - /// Gets or sets the row version. - /// - /// - /// Required, Concurrency Token. - /// + /// [ConcurrencyCheck] - public uint RowVersion { get; set; } + public uint RowVersion { get; private set; } /// - /// Gets or sets the list of access schedules this user has. + /// Gets the list of access schedules this user has. /// - public virtual ICollection AccessSchedules { get; protected set; } + public virtual ICollection AccessSchedules { get; private set; } /// - /// Gets or sets the list of item display preferences. + /// Gets the list of item display preferences. /// - public virtual ICollection ItemDisplayPreferences { get; protected set; } + public virtual ICollection ItemDisplayPreferences { get; private set; } /* /// - /// Gets or sets the list of groups this user is a member of. + /// Gets the list of groups this user is a member of. /// - [ForeignKey("Group_Groups_Guid")] - public virtual ICollection Groups { get; protected set; } + public virtual ICollection Groups { get; private set; } */ /// - /// Gets or sets the list of permissions this user has. + /// Gets the list of permissions this user has. /// [ForeignKey("Permission_Permissions_Guid")] - public virtual ICollection Permissions { get; protected set; } + public virtual ICollection Permissions { get; private set; } /* /// - /// Gets or sets the list of provider mappings this user has. + /// Gets the list of provider mappings this user has. /// - [ForeignKey("ProviderMapping_ProviderMappings_Id")] - public virtual ICollection ProviderMappings { get; protected set; } + public virtual ICollection ProviderMappings { get; private set; } */ /// - /// Gets or sets the list of preferences this user has. + /// Gets the list of preferences this user has. /// [ForeignKey("Preference_Preferences_Guid")] - public virtual ICollection Preferences { get; protected set; } + public virtual ICollection Preferences { get; private set; } /// public void OnSavingChanges() From 3ffef5794ea6ddda565d89a58667980f1c3bddc3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 17 Mar 2021 19:21:03 -0400 Subject: [PATCH 183/402] Delete unnecessary indexes Multicolumn indexes can be queried on the first column without needing a separate index --- Jellyfin.Server.Implementations/JellyfinDb.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 39f842354..eda2fa65c 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -149,18 +149,10 @@ namespace Jellyfin.Server.Implementations modelBuilder.HasDefaultSchema("jellyfin"); - modelBuilder.Entity() - .HasIndex(entity => entity.UserId) - .IsUnique(false); - modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client }) .IsUnique(); - modelBuilder.Entity() - .HasIndex(entity => entity.UserId) - .IsUnique(false); - modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client, entity.Key }) .IsUnique(); From 0a579e5bbd3c36e15d322ee5d30c8ef3331863bf Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 17 Mar 2021 21:41:52 -0400 Subject: [PATCH 184/402] Configure user deletion behavior --- Jellyfin.Server.Implementations/JellyfinDb.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index eda2fa65c..88ed22086 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -149,6 +149,31 @@ namespace Jellyfin.Server.Implementations modelBuilder.HasDefaultSchema("jellyfin"); + modelBuilder.Entity() + .HasOne(u => u.ProfileImage) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity() + .HasMany(u => u.Permissions) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity() + .HasMany(u => u.Preferences) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity() + .HasMany(u => u.AccessSchedules) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity() + .HasMany(u => u.DisplayPreferences) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client }) .IsUnique(); From f1cadb27d9d6ddbb2d42777e4c546a73bd5bcfe3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 18 Mar 2021 15:37:36 -0400 Subject: [PATCH 185/402] Add id properties for preferences and permissions --- Jellyfin.Data/Entities/Permission.cs | 6 ++++++ Jellyfin.Data/Entities/Preference.cs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index 005d1a10f..2c5894318 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -1,5 +1,6 @@ #pragma warning disable CA1711 // Identifiers should not have incorrect suffix +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; @@ -33,6 +34,11 @@ namespace Jellyfin.Data.Entities [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; private set; } + /// + /// Gets or sets the id of the associated user. + /// + public Guid UserId { get; set; } + /// /// Gets the type of this permission. /// diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 048caecee..a7d93f32c 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -32,6 +32,11 @@ namespace Jellyfin.Data.Entities [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; private set; } + /// + /// Gets or sets the id of the associated user. + /// + public Guid UserId { get; set; } + /// /// Gets the type of this preference. /// From 3c4187e7803afd1d95eac20b30d2063ca2134413 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 18 Mar 2021 20:09:11 -0400 Subject: [PATCH 186/402] Add indexes for user permissions and preferences --- Jellyfin.Server.Implementations/JellyfinDb.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 88ed22086..a2eaea3ca 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -174,6 +174,7 @@ namespace Jellyfin.Server.Implementations .WithOne() .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client }) .IsUnique(); @@ -181,6 +182,19 @@ namespace Jellyfin.Server.Implementations modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client, entity.Key }) .IsUnique(); + + // Used to get a user's permissions or a specific permission for a user. + // Also prevents multiple values being created for a user. + // Filtered over non-null user ids for when other entities (groups, API keys) get permissions + modelBuilder.Entity() + .HasIndex(p => new { p.UserId, p.Kind }) + .HasFilter("[UserId] IS NOT NULL") + .IsUnique(); + + modelBuilder.Entity() + .HasIndex(p => new { p.UserId, p.Kind }) + .HasFilter("[UserId] IS NOT NULL") + .IsUnique(); } } } From a07ad7122281247a48c1f8a48588f49c6f64f2c4 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 18 Mar 2021 20:24:37 -0400 Subject: [PATCH 187/402] Use NOCASE collation and index on username field --- Jellyfin.Server.Implementations/JellyfinDb.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index a2eaea3ca..2ee6f625f 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -149,6 +149,14 @@ namespace Jellyfin.Server.Implementations modelBuilder.HasDefaultSchema("jellyfin"); + // Collations + + modelBuilder.Entity() + .Property(user => user.Username) + .UseCollation("NOCASE"); + + // Delete behavior + modelBuilder.Entity() .HasOne(u => u.ProfileImage) .WithOne() @@ -174,6 +182,11 @@ namespace Jellyfin.Server.Implementations .WithOne() .OnDelete(DeleteBehavior.Cascade); + // Indexes + + modelBuilder.Entity() + .HasIndex(entity => entity.Username) + .IsUnique(); modelBuilder.Entity() .HasIndex(entity => new { entity.UserId, entity.ItemId, entity.Client }) From ea0a9c2cca4fd0f6ff84f79420281a7738602e60 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 19 Mar 2021 00:26:00 -0400 Subject: [PATCH 188/402] Properly configure foreign keys --- Jellyfin.Server.Implementations/JellyfinDb.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 2ee6f625f..499d99a50 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -165,11 +165,13 @@ namespace Jellyfin.Server.Implementations modelBuilder.Entity() .HasMany(u => u.Permissions) .WithOne() + .HasForeignKey(p => p.UserId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(u => u.Preferences) .WithOne() + .HasForeignKey(p => p.UserId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() From daa21c9e99a8b7b6e9a76c130f8e0943692bd1da Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 19 Mar 2021 00:26:07 -0400 Subject: [PATCH 189/402] Add migration --- Jellyfin.Data/Entities/Permission.cs | 2 +- Jellyfin.Data/Entities/Preference.cs | 2 +- Jellyfin.Server.Implementations/JellyfinDb.cs | 10 + ...181425_AddIndexesAndCollations.Designer.cs | 535 ++++++++++++++++++ .../20210320181425_AddIndexesAndCollations.cs | 240 ++++++++ .../Migrations/JellyfinDbModelSnapshot.cs | 42 +- 6 files changed, 814 insertions(+), 17 deletions(-) create mode 100644 Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index 2c5894318..6d2e68077 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Data.Entities /// /// Gets or sets the id of the associated user. /// - public Guid UserId { get; set; } + public Guid? UserId { get; set; } /// /// Gets the type of this permission. diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index a7d93f32c..a6ab275d3 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -35,7 +35,7 @@ namespace Jellyfin.Data.Entities /// /// Gets or sets the id of the associated user. /// - public Guid UserId { get; set; } + public Guid? UserId { get; set; } /// /// Gets the type of this preference. diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 499d99a50..db648472d 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -184,6 +184,16 @@ namespace Jellyfin.Server.Implementations .WithOne() .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasMany(u => u.ItemDisplayPreferences) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity() + .HasMany(d => d.HomeSections) + .WithOne() + .OnDelete(DeleteBehavior.Cascade); + // Indexes modelBuilder.Entity() diff --git a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs new file mode 100644 index 000000000..869676824 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs @@ -0,0 +1,535 @@ +#pragma warning disable CS1591 + +// +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + [Migration("20210320181425_AddIndexesAndCollations")] + partial class AddIndexesAndCollations + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("EndHour") + .HasColumnType("REAL"); + + b.Property("StartHour") + .HasColumnType("REAL"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccessSchedules"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateCreated") + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("ShortOverview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "ItemId", "Client", "Key") + .IsUnique(); + + b.ToTable("CustomItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChromecastVersion") + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("DashboardTheme") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("EnableNextVideoInfoOverlay") + .HasColumnType("INTEGER"); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("ScrollDirection") + .HasColumnType("INTEGER"); + + b.Property("ShowBackdrop") + .HasColumnType("INTEGER"); + + b.Property("ShowSidebar") + .HasColumnType("INTEGER"); + + b.Property("SkipBackwardLength") + .HasColumnType("INTEGER"); + + b.Property("SkipForwardLength") + .HasColumnType("INTEGER"); + + b.Property("TvHome") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "ItemId", "Client") + .IsUnique(); + + b.ToTable("DisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DisplayPreferencesId") + .HasColumnType("INTEGER"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisplayPreferencesId"); + + b.ToTable("HomeSection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("ImageInfos"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("RememberIndexing") + .HasColumnType("INTEGER"); + + b.Property("RememberSorting") + .HasColumnType("INTEGER"); + + b.Property("SortBy") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("ViewType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Permission_Permissions_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Kind") + .IsUnique() + .HasFilter("[UserId] IS NOT NULL"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Preference_Preferences_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Kind") + .IsUnique() + .HasFilter("[UserId] IS NOT NULL"); + + b.ToTable("Preferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AudioLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("AuthenticationProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property("EasyPassword") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property("EnableAutoLogin") + .HasColumnType("INTEGER"); + + b.Property("EnableLocalPassword") + .HasColumnType("INTEGER"); + + b.Property("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property("InternalId") + .HasColumnType("INTEGER"); + + b.Property("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property("LastLoginDate") + .HasColumnType("TEXT"); + + b.Property("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property("MaxActiveSessions") + .HasColumnType("INTEGER"); + + b.Property("MaxParentalAgeRating") + .HasColumnType("INTEGER"); + + b.Property("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property("PasswordResetProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property("RemoteClientBitrateLimit") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SubtitleLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("SubtitleMode") + .HasColumnType("INTEGER"); + + b.Property("SyncPlayAccess") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT") + .UseCollation("NOCASE"); + + b.HasKey("Id"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("AccessSchedules") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("DisplayPreferences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null) + .WithMany("HomeSections") + .HasForeignKey("DisplayPreferencesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("ProfileImage") + .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("ItemDisplayPreferences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Permissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Preferences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Navigation("HomeSections"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Navigation("AccessSchedules"); + + b.Navigation("DisplayPreferences"); + + b.Navigation("ItemDisplayPreferences"); + + b.Navigation("Permissions"); + + b.Navigation("Preferences"); + + b.Navigation("ProfileImage"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs new file mode 100644 index 000000000..506e4ae66 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.cs @@ -0,0 +1,240 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class AddIndexesAndCollations : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ImageInfos_Users_UserId", + schema: "jellyfin", + table: "ImageInfos"); + + migrationBuilder.DropForeignKey( + name: "FK_Permissions_Users_Permission_Permissions_Guid", + schema: "jellyfin", + table: "Permissions"); + + migrationBuilder.DropForeignKey( + name: "FK_Preferences_Users_Preference_Preferences_Guid", + schema: "jellyfin", + table: "Preferences"); + + migrationBuilder.DropIndex( + name: "IX_Preferences_Preference_Preferences_Guid", + schema: "jellyfin", + table: "Preferences"); + + migrationBuilder.DropIndex( + name: "IX_Permissions_Permission_Permissions_Guid", + schema: "jellyfin", + table: "Permissions"); + + migrationBuilder.DropIndex( + name: "IX_DisplayPreferences_UserId", + schema: "jellyfin", + table: "DisplayPreferences"); + + migrationBuilder.DropIndex( + name: "IX_CustomItemDisplayPreferences_UserId", + schema: "jellyfin", + table: "CustomItemDisplayPreferences"); + + migrationBuilder.AlterColumn( + name: "Username", + schema: "jellyfin", + table: "Users", + type: "TEXT", + maxLength: 255, + nullable: false, + collation: "NOCASE", + oldClrType: typeof(string), + oldType: "TEXT", + oldMaxLength: 255); + + migrationBuilder.AddColumn( + name: "UserId", + schema: "jellyfin", + table: "Preferences", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "UserId", + schema: "jellyfin", + table: "Permissions", + type: "TEXT", + nullable: true); + + migrationBuilder.Sql("UPDATE Preferences SET UserId = Preference_Preferences_Guid"); + migrationBuilder.Sql("UPDATE Permissions SET UserId = Permission_Permissions_Guid"); + + migrationBuilder.CreateIndex( + name: "IX_Users_Username", + schema: "jellyfin", + table: "Users", + column: "Username", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Preferences_UserId_Kind", + schema: "jellyfin", + table: "Preferences", + columns: new[] { "UserId", "Kind" }, + unique: true, + filter: "[UserId] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_Permissions_UserId_Kind", + schema: "jellyfin", + table: "Permissions", + columns: new[] { "UserId", "Kind" }, + unique: true, + filter: "[UserId] IS NOT NULL"); + + migrationBuilder.AddForeignKey( + name: "FK_ImageInfos_Users_UserId", + schema: "jellyfin", + table: "ImageInfos", + column: "UserId", + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Permissions_Users_UserId", + schema: "jellyfin", + table: "Permissions", + column: "UserId", + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Preferences_Users_UserId", + schema: "jellyfin", + table: "Preferences", + column: "UserId", + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ImageInfos_Users_UserId", + schema: "jellyfin", + table: "ImageInfos"); + + migrationBuilder.DropForeignKey( + name: "FK_Permissions_Users_UserId", + schema: "jellyfin", + table: "Permissions"); + + migrationBuilder.DropForeignKey( + name: "FK_Preferences_Users_UserId", + schema: "jellyfin", + table: "Preferences"); + + migrationBuilder.DropIndex( + name: "IX_Users_Username", + schema: "jellyfin", + table: "Users"); + + migrationBuilder.DropIndex( + name: "IX_Preferences_UserId_Kind", + schema: "jellyfin", + table: "Preferences"); + + migrationBuilder.DropIndex( + name: "IX_Permissions_UserId_Kind", + schema: "jellyfin", + table: "Permissions"); + + migrationBuilder.DropColumn( + name: "UserId", + schema: "jellyfin", + table: "Preferences"); + + migrationBuilder.DropColumn( + name: "UserId", + schema: "jellyfin", + table: "Permissions"); + + migrationBuilder.AlterColumn( + name: "Username", + schema: "jellyfin", + table: "Users", + type: "TEXT", + maxLength: 255, + nullable: false, + oldClrType: typeof(string), + oldType: "TEXT", + oldMaxLength: 255, + oldCollation: "NOCASE"); + + migrationBuilder.CreateIndex( + name: "IX_Preferences_Preference_Preferences_Guid", + schema: "jellyfin", + table: "Preferences", + column: "Preference_Preferences_Guid"); + + migrationBuilder.CreateIndex( + name: "IX_Permissions_Permission_Permissions_Guid", + schema: "jellyfin", + table: "Permissions", + column: "Permission_Permissions_Guid"); + + migrationBuilder.CreateIndex( + name: "IX_DisplayPreferences_UserId", + schema: "jellyfin", + table: "DisplayPreferences", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_CustomItemDisplayPreferences_UserId", + schema: "jellyfin", + table: "CustomItemDisplayPreferences", + column: "UserId"); + + migrationBuilder.AddForeignKey( + name: "FK_ImageInfos_Users_UserId", + schema: "jellyfin", + table: "ImageInfos", + column: "UserId", + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Permissions_Users_Permission_Permissions_Guid", + schema: "jellyfin", + table: "Permissions", + column: "Permission_Permissions_Guid", + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Preferences_Users_Preference_Preferences_Guid", + schema: "jellyfin", + table: "Preferences", + column: "Preference_Preferences_Guid", + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 1614a88ef..d8e13ba0c 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("jellyfin") - .HasAnnotation("ProductVersion", "5.0.0"); + .HasAnnotation("ProductVersion", "5.0.3"); modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => { @@ -115,8 +115,6 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasKey("Id"); - b.HasIndex("UserId"); - b.HasIndex("UserId", "ItemId", "Client", "Key") .IsUnique(); @@ -174,8 +172,6 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasKey("Id"); - b.HasIndex("UserId"); - b.HasIndex("UserId", "ItemId", "Client") .IsUnique(); @@ -289,12 +285,17 @@ namespace Jellyfin.Server.Implementations.Migrations .IsConcurrencyToken() .HasColumnType("INTEGER"); + b.Property("UserId") + .HasColumnType("TEXT"); + b.Property("Value") .HasColumnType("INTEGER"); b.HasKey("Id"); - b.HasIndex("Permission_Permissions_Guid"); + b.HasIndex("UserId", "Kind") + .IsUnique() + .HasFilter("[UserId] IS NOT NULL"); b.ToTable("Permissions"); }); @@ -315,6 +316,9 @@ namespace Jellyfin.Server.Implementations.Migrations .IsConcurrencyToken() .HasColumnType("INTEGER"); + b.Property("UserId") + .HasColumnType("TEXT"); + b.Property("Value") .IsRequired() .HasMaxLength(65535) @@ -322,7 +326,9 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasKey("Id"); - b.HasIndex("Preference_Preferences_Guid"); + b.HasIndex("UserId", "Kind") + .IsUnique() + .HasFilter("[UserId] IS NOT NULL"); b.ToTable("Preferences"); }); @@ -429,10 +435,14 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("Username") .IsRequired() .HasMaxLength(255) - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .UseCollation("NOCASE"); b.HasKey("Id"); + b.HasIndex("Username") + .IsUnique(); + b.ToTable("Users"); }); @@ -448,8 +458,8 @@ namespace Jellyfin.Server.Implementations.Migrations modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => { b.HasOne("Jellyfin.Data.Entities.User", null) - .WithOne("DisplayPreferences") - .HasForeignKey("Jellyfin.Data.Entities.DisplayPreferences", "UserId") + .WithMany("DisplayPreferences") + .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); @@ -467,7 +477,8 @@ namespace Jellyfin.Server.Implementations.Migrations { b.HasOne("Jellyfin.Data.Entities.User", null) .WithOne("ProfileImage") - .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId"); + .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => @@ -483,14 +494,16 @@ namespace Jellyfin.Server.Implementations.Migrations { b.HasOne("Jellyfin.Data.Entities.User", null) .WithMany("Permissions") - .HasForeignKey("Permission_Permissions_Guid"); + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => { b.HasOne("Jellyfin.Data.Entities.User", null) .WithMany("Preferences") - .HasForeignKey("Preference_Preferences_Guid"); + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => @@ -502,8 +515,7 @@ namespace Jellyfin.Server.Implementations.Migrations { b.Navigation("AccessSchedules"); - b.Navigation("DisplayPreferences") - .IsRequired(); + b.Navigation("DisplayPreferences"); b.Navigation("ItemDisplayPreferences"); From a7b29e2fe0bb08f4b8f37fa5aac7af66c7cb00e8 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 25 Mar 2021 19:48:30 -0400 Subject: [PATCH 190/402] Clean up user renaming --- Jellyfin.Server.Implementations/Users/UserManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 50d7612f2..f9a1a8ee9 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -144,7 +144,13 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("The new and old names must be different."); } - if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase))) + await using var dbContext = _dbProvider.CreateContext(); + + if (await dbContext.Users + .AsQueryable() + .Where(u => u.Username == newName && u.Id != user.Id) + .AnyAsync() + .ConfigureAwait(false)) { throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, From 7364155579c52e5019cdcb71edc03546babe3fb3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 25 Mar 2021 19:49:52 -0400 Subject: [PATCH 191/402] Clean up user deletion --- Jellyfin.Server.Implementations/Users/UserManager.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index f9a1a8ee9..b6b45e69b 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -257,16 +257,6 @@ namespace Jellyfin.Server.Implementations.Users } await using var dbContext = _dbProvider.CreateContext(); - - // Clear all entities related to the user from the database. - if (user.ProfileImage != null) - { - dbContext.Remove(user.ProfileImage); - } - - dbContext.RemoveRange(user.Permissions); - dbContext.RemoveRange(user.Preferences); - dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); await dbContext.SaveChangesAsync().ConfigureAwait(false); _users.Remove(userId); From 73fe9d3f696e5d144047e173553b1d1dba03c5d1 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 25 Mar 2021 18:06:25 -0600 Subject: [PATCH 192/402] Allow subtitle format to be set from query parameter. --- Jellyfin.Api/Controllers/SubtitleController.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index 16a47f2d8..e260efc9a 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -185,6 +185,7 @@ namespace Jellyfin.Api.Controllers /// The item id. /// The media source id. /// The subtitle stream index. + /// The (route) format of the returned subtitle. /// The format of the returned subtitle. /// Optional. The end position of the subtitle in ticks. /// Optional. Whether to copy the timestamps. @@ -192,19 +193,25 @@ namespace Jellyfin.Api.Controllers /// Optional. The start position of the subtitle in ticks. /// File returned. /// A with the subtitle file. - [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{format}")] + [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{routeFormat}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile("text/*")] public async Task GetSubtitle( [FromRoute, Required] Guid itemId, [FromRoute, Required] string mediaSourceId, [FromRoute, Required] int index, - [FromRoute, Required] string format, + [FromRoute, Required] string routeFormat, + [FromQuery] string? format, [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false, [FromQuery] long startPositionTicks = 0) { + if (string.IsNullOrEmpty(format)) + { + format = routeFormat; + } + if (string.Equals(format, "js", StringComparison.OrdinalIgnoreCase)) { format = "json"; @@ -255,13 +262,14 @@ namespace Jellyfin.Api.Controllers /// The media source id. /// The subtitle stream index. /// Optional. The start position of the subtitle in ticks. + /// The (route) format of the returned subtitle. /// The format of the returned subtitle. /// Optional. The end position of the subtitle in ticks. /// Optional. Whether to copy the timestamps. /// Optional. Whether to add a VTT time map. /// File returned. /// A with the subtitle file. - [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks}/Stream.{format}")] + [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks}/Stream.{routeFormat}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile("text/*")] public Task GetSubtitleWithTicks( @@ -269,7 +277,8 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string mediaSourceId, [FromRoute, Required] int index, [FromRoute, Required] long startPositionTicks, - [FromRoute, Required] string format, + [FromRoute, Required] string routeFormat, + [FromQuery] string? format, [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false) @@ -278,6 +287,7 @@ namespace Jellyfin.Api.Controllers itemId, mediaSourceId, index, + routeFormat, format, endPositionTicks, copyTimestamps, From e0ff51cf2af1d4b90e572ab3e012746748909ed8 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 25 Mar 2021 20:30:15 -0600 Subject: [PATCH 193/402] Mark query parameters as obsolete --- Jellyfin.Api/Controllers/SubtitleController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index e260efc9a..a21d377ac 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string mediaSourceId, [FromRoute, Required] int index, [FromRoute, Required] string routeFormat, - [FromQuery] string? format, + [FromQuery, ParameterObsolete] string? format, [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false, @@ -278,7 +278,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] int index, [FromRoute, Required] long startPositionTicks, [FromRoute, Required] string routeFormat, - [FromQuery] string? format, + [FromQuery, ParameterObsolete] string? format, [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false) From db2fbcef2b6398dc845b69affde0aef2cc432056 Mon Sep 17 00:00:00 2001 From: lmaonator Date: Fri, 26 Mar 2021 12:15:44 +0100 Subject: [PATCH 194/402] Fix stream selection having no effect when casting When casting to jellyfin-mpv-shim from jellyfin-web in the browser, jellyfin-web sends data about which version (for grouped items) and which streams the user selected in the browser to the "Sessions/{sessionId}/Playing" API endpoint. The API endpoint currently doesn't forward them to jellyfin-mpv-shim through the Play command, which results in the default streams being played instead of the browser selected ones. PlayRequest already has the properties and they are already sent to the cast client by SendPlayCommand when present. jellyfin-mpv-shim will already use them to select the wanted streams when it receives the Play command. All that's needed to make it work is to take the parameters and assign them to PlayRequest. --- Jellyfin.Api/Controllers/SessionController.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index e2269a2ce..e178e3389 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -153,6 +153,10 @@ namespace Jellyfin.Api.Controllers /// The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now. /// The ids of the items to play, comma delimited. /// The starting position of the first item. + /// Optional. The media source id. + /// Optional. The index of the audio stream to play. + /// Optional. The index of the subtitle stream to play. + /// Optional. The start index. /// Instruction sent to session. /// A . [HttpPost("Sessions/{sessionId}/Playing")] @@ -162,13 +166,21 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromQuery, Required] PlayCommand playCommand, [FromQuery, Required, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] itemIds, - [FromQuery] long? startPositionTicks) + [FromQuery] long? startPositionTicks, + [FromQuery] string mediaSourceId, + [FromQuery] int? audioStreamIndex, + [FromQuery] int? subtitleStreamIndex, + [FromQuery] int? startIndex) { var playRequest = new PlayRequest { ItemIds = itemIds, StartPositionTicks = startPositionTicks, - PlayCommand = playCommand + PlayCommand = playCommand, + MediaSourceId = mediaSourceId, + AudioStreamIndex = audioStreamIndex, + SubtitleStreamIndex = subtitleStreamIndex, + StartIndex = startIndex }; _sessionManager.SendPlayCommand( From 5b758c47119c0520ff543f7b8e812f67533ce4f2 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 26 Mar 2021 07:07:45 -0600 Subject: [PATCH 195/402] Mark query parameters as obsolete --- .../Controllers/SubtitleController.cs | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index a21d377ac..7a44b26a4 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -182,35 +182,42 @@ namespace Jellyfin.Api.Controllers /// /// Gets subtitles in a specified format. /// + /// The (route) item id. + /// The (route) media source id. + /// The (route) subtitle stream index. + /// The (route) format of the returned subtitle. /// The item id. /// The media source id. /// The subtitle stream index. - /// The (route) format of the returned subtitle. /// The format of the returned subtitle. /// Optional. The end position of the subtitle in ticks. /// Optional. Whether to copy the timestamps. /// Optional. Whether to add a VTT time map. - /// Optional. The start position of the subtitle in ticks. + /// The start position of the subtitle in ticks. /// File returned. /// A with the subtitle file. [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{routeFormat}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile("text/*")] public async Task GetSubtitle( - [FromRoute, Required] Guid itemId, - [FromRoute, Required] string mediaSourceId, - [FromRoute, Required] int index, + [FromRoute, Required] Guid routeItemId, + [FromRoute, Required] string routeMediaSourceId, + [FromRoute, Required] int routeIndex, [FromRoute, Required] string routeFormat, + [FromQuery, ParameterObsolete] Guid? itemId, + [FromQuery, ParameterObsolete] string? mediaSourceId, + [FromQuery, ParameterObsolete] int? index, [FromQuery, ParameterObsolete] string? format, [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false, [FromQuery] long startPositionTicks = 0) { - if (string.IsNullOrEmpty(format)) - { - format = routeFormat; - } + // Set parameters to route value if not provided via query. + itemId ??= routeItemId; + mediaSourceId ??= routeMediaSourceId; + index ??= routeIndex; + format ??= routeFormat; if (string.Equals(format, "js", StringComparison.OrdinalIgnoreCase)) { @@ -219,9 +226,9 @@ namespace Jellyfin.Api.Controllers if (string.IsNullOrEmpty(format)) { - var item = (Video)_libraryManager.GetItemById(itemId); + var item = (Video)_libraryManager.GetItemById(itemId.Value); - var idString = itemId.ToString("N", CultureInfo.InvariantCulture); + var idString = itemId.Value.ToString("N", CultureInfo.InvariantCulture); var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false) .First(i => string.Equals(i.Id, mediaSourceId ?? idString, StringComparison.Ordinal)); @@ -233,7 +240,7 @@ namespace Jellyfin.Api.Controllers if (string.Equals(format, "vtt", StringComparison.OrdinalIgnoreCase) && addVttTimeMap) { - await using Stream stream = await EncodeSubtitles(itemId, mediaSourceId, index, format, startPositionTicks, endPositionTicks, copyTimestamps).ConfigureAwait(false); + await using Stream stream = await EncodeSubtitles(itemId.Value, mediaSourceId, index.Value, format, startPositionTicks, endPositionTicks, copyTimestamps).ConfigureAwait(false); using var reader = new StreamReader(stream); var text = await reader.ReadToEndAsync().ConfigureAwait(false); @@ -245,9 +252,9 @@ namespace Jellyfin.Api.Controllers return File( await EncodeSubtitles( - itemId, + itemId.Value, mediaSourceId, - index, + index.Value, format, startPositionTicks, endPositionTicks, @@ -258,41 +265,52 @@ namespace Jellyfin.Api.Controllers /// /// Gets subtitles in a specified format. /// + /// The (route) item id. + /// The (route) media source id. + /// The (route) subtitle stream index. + /// The (route) start position of the subtitle in ticks. + /// The (route) format of the returned subtitle. /// The item id. /// The media source id. /// The subtitle stream index. - /// Optional. The start position of the subtitle in ticks. - /// The (route) format of the returned subtitle. + /// The start position of the subtitle in ticks. /// The format of the returned subtitle. /// Optional. The end position of the subtitle in ticks. /// Optional. Whether to copy the timestamps. /// Optional. Whether to add a VTT time map. /// File returned. /// A with the subtitle file. - [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks}/Stream.{routeFormat}")] + [HttpGet("Videos/{routeItemId}/{routeMediaSourceId}/Subtitles/{routeIndex}/{routeStartPositionTicks}/Stream.{routeFormat}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile("text/*")] public Task GetSubtitleWithTicks( - [FromRoute, Required] Guid itemId, - [FromRoute, Required] string mediaSourceId, - [FromRoute, Required] int index, - [FromRoute, Required] long startPositionTicks, + [FromRoute, Required] Guid routeItemId, + [FromRoute, Required] string routeMediaSourceId, + [FromRoute, Required] int routeIndex, + [FromRoute, Required] long routeStartPositionTicks, [FromRoute, Required] string routeFormat, + [FromQuery, ParameterObsolete] Guid? itemId, + [FromQuery, ParameterObsolete] string? mediaSourceId, + [FromQuery, ParameterObsolete] int? index, + [FromQuery, ParameterObsolete] long? startPositionTicks, [FromQuery, ParameterObsolete] string? format, [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false) { return GetSubtitle( + routeItemId, + routeMediaSourceId, + routeIndex, + routeFormat, itemId, mediaSourceId, index, - routeFormat, format, endPositionTicks, copyTimestamps, addVttTimeMap, - startPositionTicks); + startPositionTicks ?? routeStartPositionTicks); } /// From 694d772b113b3f734e559b1dd083823dd931aad6 Mon Sep 17 00:00:00 2001 From: lmaonator <33778605+lmaonator@users.noreply.github.com> Date: Fri, 26 Mar 2021 14:13:45 +0100 Subject: [PATCH 196/402] Update Jellyfin.Api/Controllers/SessionController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/SessionController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index e178e3389..0703d4255 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -167,7 +167,7 @@ namespace Jellyfin.Api.Controllers [FromQuery, Required] PlayCommand playCommand, [FromQuery, Required, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] itemIds, [FromQuery] long? startPositionTicks, - [FromQuery] string mediaSourceId, + [FromQuery] string? mediaSourceId, [FromQuery] int? audioStreamIndex, [FromQuery] int? subtitleStreamIndex, [FromQuery] int? startIndex) From 78f7fdeaccee7f321740ede24dee797622b44f8b Mon Sep 17 00:00:00 2001 From: David Date: Fri, 26 Mar 2021 17:06:01 +0100 Subject: [PATCH 197/402] Rename methods and optimize allocations --- .../Library/PathExtensions.cs | 4 +-- .../Providers/ProviderIdParsers.cs | 34 ++++++++++--------- .../Parsers/BaseNfoParser.cs | 16 ++++----- .../Providers/ProviderIdParserTests.cs | 19 ++++++----- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 72fe5a854..73236bad8 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -44,8 +44,8 @@ namespace Emby.Server.Implementations.Library // for imdbid we also accept pattern matching if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase)) { - var match = ProviderIdParsers.TryParseImdbId(str, out var imdbId); - return match ? imdbId : null; + var match = ProviderIdParsers.TryFindImdbId(str, out var imdbId); + return match ? imdbId.ToString() : null; } return null; diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs index 7744124ea..26eaccac5 100644 --- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Common.Providers { private const int ImdbMinNumbers = 7; private const int ImdbMaxNumbers = 8; + private const string ImdbPrefix = "tt"; /// /// Parses an IMDb id from a string. @@ -19,9 +20,9 @@ namespace MediaBrowser.Common.Providers /// The text to parse. /// The parsed IMDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseImdbId(ReadOnlySpan text, [NotNullWhen(true)] out string? imdbId) + public static bool TryFindImdbId(ReadOnlySpan text, [NotNullWhen(true)] out ReadOnlySpan imdbId) { - var tt = "tt".AsSpan(); + var tt = ImdbPrefix.AsSpan(); // imdb id is at least 9 chars (tt + 7 numbers) while (text.Length >= 2 + ImdbMinNumbers) @@ -33,9 +34,10 @@ namespace MediaBrowser.Common.Providers return false; } - text = text.Slice(ttPos + tt.Length); - var i = 0; - for (; i < Math.Min(text.Length, ImdbMaxNumbers); i++) + text = text.Slice(ttPos); + var i = 2; + var limit = Math.Min(text.Length, ImdbMaxNumbers + 2); + for (; i < limit; i++) { var c = text[i]; if (!IsDigit(c)) @@ -44,10 +46,10 @@ namespace MediaBrowser.Common.Providers } } - // skip if more than 8 digits - if (i <= ImdbMaxNumbers && i >= ImdbMinNumbers) + // skip if more than 8 digits + 2 chars for tt + if (i <= ImdbMaxNumbers + 2 && i >= ImdbMinNumbers + 2) { - imdbId = string.Concat(tt, text.Slice(0, i)); + imdbId = text.Slice(0, i); return true; } @@ -64,8 +66,8 @@ namespace MediaBrowser.Common.Providers /// The text with the url to parse. /// The parsed TMDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseTmdbMovieId(ReadOnlySpan text, [NotNullWhen(true)] out string? tmdbId) - => TryParseProviderId(text, "themoviedb.org/movie/", out tmdbId); + public static bool TryFindTmdbMovieId(ReadOnlySpan text, [NotNullWhen(true)] out ReadOnlySpan tmdbId) + => TryFindProviderId(text, "themoviedb.org/movie/", out tmdbId); /// /// Parses an TMDb id from a series url. @@ -73,8 +75,8 @@ namespace MediaBrowser.Common.Providers /// The text with the url to parse. /// The parsed TMDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseTmdbSeriesId(ReadOnlySpan text, [NotNullWhen(true)] out string? tmdbId) - => TryParseProviderId(text, "themoviedb.org/tv/", out tmdbId); + public static bool TryFindTmdbSeriesId(ReadOnlySpan text, [NotNullWhen(true)] out ReadOnlySpan tmdbId) + => TryFindProviderId(text, "themoviedb.org/tv/", out tmdbId); /// /// Parses an TVDb id from a url. @@ -82,10 +84,10 @@ namespace MediaBrowser.Common.Providers /// The text with the url to parse. /// The parsed TVDb id. /// True if parsing was successful, false otherwise. - public static bool TryParseTvdbId(ReadOnlySpan text, [NotNullWhen(true)] out string? tvdbId) - => TryParseProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId); + public static bool TryFindTvdbId(ReadOnlySpan text, [NotNullWhen(true)] out ReadOnlySpan tvdbId) + => TryFindProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId); - private static bool TryParseProviderId(ReadOnlySpan text, ReadOnlySpan searchString, [NotNullWhen(true)] out string? providerId) + private static bool TryFindProviderId(ReadOnlySpan text, ReadOnlySpan searchString, [NotNullWhen(true)] out ReadOnlySpan providerId) { var searchPos = text.IndexOf(searchString); if (searchPos == -1) @@ -109,7 +111,7 @@ namespace MediaBrowser.Common.Providers if (i >= 1) { - providerId = text.Slice(0, i).ToString(); + providerId = text.Slice(0, i); return true; } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index d2fa120e8..c5627f880 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -219,29 +219,29 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected void ParseProviderLinks(T item, string xml) { - if (ProviderIdParsers.TryParseImdbId(xml, out var imdbId)) + if (ProviderIdParsers.TryFindImdbId(xml, out var imdbId)) { - item.SetProviderId(MetadataProvider.Imdb, imdbId); + item.SetProviderId(MetadataProvider.Imdb, imdbId.ToString()); } if (item is Movie) { - if (ProviderIdParsers.TryParseTmdbMovieId(xml, out var tmdbId)) + if (ProviderIdParsers.TryFindTmdbMovieId(xml, out var tmdbId)) { - item.SetProviderId(MetadataProvider.Tmdb, tmdbId); + item.SetProviderId(MetadataProvider.Tmdb, tmdbId.ToString()); } } if (item is Series) { - if (ProviderIdParsers.TryParseTmdbSeriesId(xml, out var tmdbId)) + if (ProviderIdParsers.TryFindTmdbSeriesId(xml, out var tmdbId)) { - item.SetProviderId(MetadataProvider.Tmdb, tmdbId); + item.SetProviderId(MetadataProvider.Tmdb, tmdbId.ToString()); } - if (ProviderIdParsers.TryParseTvdbId(xml, out var tvdbId)) + if (ProviderIdParsers.TryFindTvdbId(xml, out var tvdbId)) { - item.SetProviderId(MetadataProvider.Tvdb, tvdbId); + item.SetProviderId(MetadataProvider.Tvdb, tvdbId.ToString()); } } } diff --git a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs index cfe1ea86b..1ce54c59b 100644 --- a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs +++ b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Providers; +using System; +using MediaBrowser.Common.Providers; using Xunit; namespace Jellyfin.Common.Tests.Providers @@ -20,9 +21,9 @@ namespace Jellyfin.Common.Tests.Providers [InlineData("tt123456789", true, "tt12345678")] public void Parse_Imdb(string text, bool shouldSucceed, string? imdbId) { - var succeeded = ProviderIdParsers.TryParseImdbId(text, out string? parsedId); + var succeeded = ProviderIdParsers.TryFindImdbId(text, out ReadOnlySpan parsedId); Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(imdbId, parsedId); + Assert.Equal(imdbId ?? Span.Empty.ToString(), parsedId.ToString()); } [Theory] @@ -32,9 +33,9 @@ namespace Jellyfin.Common.Tests.Providers [InlineData("https://www.themoviedb.org/tv/1668-friends", false, null)] public void Parse_TmdbMovie(string text, bool shouldSucceed, string? tmdbId) { - var succeeded = ProviderIdParsers.TryParseTmdbMovieId(text, out string? parsedId); + var succeeded = ProviderIdParsers.TryFindTmdbMovieId(text, out ReadOnlySpan parsedId); Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(tmdbId, parsedId); + Assert.Equal(tmdbId ?? Span.Empty.ToString(), parsedId.ToString()); } [Theory] @@ -44,9 +45,9 @@ namespace Jellyfin.Common.Tests.Providers [InlineData("https://www.themoviedb.org/movie/30287-fallo", false, null)] public void Parse_TmdbSeries(string text, bool shouldSucceed, string? tmdbId) { - var succeeded = ProviderIdParsers.TryParseTmdbSeriesId(text, out string? parsedId); + var succeeded = ProviderIdParsers.TryFindTmdbSeriesId(text, out ReadOnlySpan parsedId); Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(tmdbId, parsedId); + Assert.Equal(tmdbId ?? Span.Empty.ToString(), parsedId.ToString()); } [Theory] @@ -56,9 +57,9 @@ namespace Jellyfin.Common.Tests.Providers [InlineData("https://www.themoviedb.org/tv/1668-friends", false, null)] public void Parse_Tvdb(string text, bool shouldSucceed, string? tvdbId) { - var succeeded = ProviderIdParsers.TryParseTvdbId(text, out string? parsedId); + var succeeded = ProviderIdParsers.TryFindTvdbId(text, out ReadOnlySpan parsedId); Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(tvdbId, parsedId); + Assert.Equal(tvdbId ?? Span.Empty.ToString(), parsedId.ToString()); } } } From 7670189561302a88b576b3417594e036143ec7d7 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 27 Mar 2021 00:26:56 +0100 Subject: [PATCH 198/402] make directoryservice cache case sensitive --- .../Providers/DirectoryService.cs | 6 +- .../DirectoryServiceTests.cs | 200 ++++++++++++++++++ .../Jellyfin.Controller.Tests.csproj | 1 + 3 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index 16fd1d42b..5c92069b4 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -12,11 +12,11 @@ namespace MediaBrowser.Controller.Providers { private readonly IFileSystem _fileSystem; - private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _cache = new (StringComparer.Ordinal); - private readonly ConcurrentDictionary _fileCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _fileCache = new (StringComparer.Ordinal); - private readonly ConcurrentDictionary> _filePathCache = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary> _filePathCache = new (StringComparer.Ordinal); public DirectoryService(IFileSystem fileSystem) { diff --git a/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs new file mode 100644 index 000000000..feffb50e8 --- /dev/null +++ b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs @@ -0,0 +1,200 @@ +using System.Linq; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.IO; +using Moq; +using Xunit; + +namespace Jellyfin.Controller.Tests +{ + public class DirectoryServiceTests + { + private const string LowerCasePath = "/music/someartist"; + private const string UpperCasePath = "/music/SOMEARTIST"; + + private static readonly FileSystemMetadata[] _lowerCaseFileSystemMetadata = + { + new () + { + FullName = LowerCasePath + "/Artwork", + IsDirectory = true + }, + new () + { + FullName = LowerCasePath + "/Some Other Folder", + IsDirectory = true + }, + new () + { + FullName = LowerCasePath + "/Song 2.mp3", + IsDirectory = false + }, + new () + { + FullName = LowerCasePath + "/Song 3.mp3", + IsDirectory = false + } + }; + + private static readonly FileSystemMetadata[] _upperCaseFileSystemMetadata = + { + new () + { + FullName = UpperCasePath + "/Lyrics", + IsDirectory = true + }, + new () + { + FullName = UpperCasePath + "/Song 1.mp3", + IsDirectory = false + } + }; + + [Fact] + public void GetFileSystemEntries_GivenPathsWithDifferentCasing_CachesAll() + { + var fileSystemMock = new Mock(); + fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == UpperCasePath), false)).Returns(_upperCaseFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == LowerCasePath), false)).Returns(_lowerCaseFileSystemMetadata); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var upperCaseResult = directoryService.GetFileSystemEntries(UpperCasePath); + var lowerCaseResult = directoryService.GetFileSystemEntries(LowerCasePath); + + Assert.Equal(_upperCaseFileSystemMetadata, upperCaseResult); + Assert.Equal(_lowerCaseFileSystemMetadata, lowerCaseResult); + } + + [Fact] + public void GetFiles_GivenPathsWithDifferentCasing_ReturnsCorrectFiles() + { + var fileSystemMock = new Mock(); + fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == UpperCasePath), false)).Returns(_upperCaseFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == LowerCasePath), false)).Returns(_lowerCaseFileSystemMetadata); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var upperCaseResult = directoryService.GetFiles(UpperCasePath); + var lowerCaseResult = directoryService.GetFiles(LowerCasePath); + + Assert.Equal(_upperCaseFileSystemMetadata.Where(f => !f.IsDirectory), upperCaseResult); + Assert.Equal(_lowerCaseFileSystemMetadata.Where(f => !f.IsDirectory), lowerCaseResult); + } + + [Fact] + public void GetFile_GivenFilePathsWithDifferentCasing_ReturnsCorrectFile() + { + const string lowerCasePath = "/music/someartist/song 1.mp3"; + var lowerCaseFileSystemMetadata = new FileSystemMetadata + { + FullName = lowerCasePath, + Exists = true + }; + const string upperCasePath = "/music/SOMEARTIST/SONG 1.mp3"; + var upperCaseFileSystemMetadata = new FileSystemMetadata + { + FullName = upperCasePath, + Exists = false + }; + var fileSystemMock = new Mock(); + fileSystemMock.Setup(f => f.GetFileInfo(It.Is(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata); + fileSystemMock.Setup(f => f.GetFileInfo(It.Is(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var lowerCaseResult = directoryService.GetFile(lowerCasePath); + var upperCaseResult = directoryService.GetFile(upperCasePath); + + Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseResult); + Assert.Null(upperCaseResult); + } + + [Fact] + public void GetFile_GivenCachedPath_ReturnsCachedFile() + { + const string path = "/music/someartist/song 1.mp3"; + var cachedFileSystemMetadata = new FileSystemMetadata + { + FullName = path, + Exists = true + }; + var newFileSystemMetadata = new FileSystemMetadata + { + FullName = "/music/SOMEARTIST/song 1.mp3", + Exists = true + }; + + var fileSystemMock = new Mock(); + fileSystemMock.Setup(f => f.GetFileInfo(It.Is(x => x == path))).Returns(cachedFileSystemMetadata); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var result = directoryService.GetFile(path); + fileSystemMock.Setup(f => f.GetFileInfo(It.Is(x => x == path))).Returns(newFileSystemMetadata); + var secondResult = directoryService.GetFile(path); + + Assert.Equal(cachedFileSystemMetadata, result); + Assert.Equal(cachedFileSystemMetadata, secondResult); + } + + [Fact] + public void GetFilePaths_GivenCachedFilePathWithoutClear_ReturnsOnlyCachedPaths() + { + const string path = "/music/someartist"; + + var cachedPaths = new[] + { + "/music/someartist/song 1.mp3", + "/music/someartist/song 2.mp3", + "/music/someartist/song 3.mp3", + "/music/someartist/song 4.mp3", + }; + var newPaths = new[] + { + "/music/someartist/song 5.mp3", + "/music/someartist/song 6.mp3", + "/music/someartist/song 7.mp3", + "/music/someartist/song 8.mp3", + }; + + var fileSystemMock = new Mock(); + fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(cachedPaths); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var result = directoryService.GetFilePaths(path); + fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(newPaths); + var secondResult = directoryService.GetFilePaths(path); + + Assert.Equal(cachedPaths, result); + Assert.Equal(cachedPaths, secondResult); + } + + [Fact] + public void GetFilePaths_GivenCachedFilePathWithClear_ReturnsNewPaths() + { + const string path = "/music/someartist"; + + var cachedPaths = new[] + { + "/music/someartist/song 1.mp3", + "/music/someartist/song 2.mp3", + "/music/someartist/song 3.mp3", + "/music/someartist/song 4.mp3", + }; + var newPaths = new[] + { + "/music/someartist/song 5.mp3", + "/music/someartist/song 6.mp3", + "/music/someartist/song 7.mp3", + "/music/someartist/song 8.mp3", + }; + + var fileSystemMock = new Mock(); + fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(cachedPaths); + var directoryService = new DirectoryService(fileSystemMock.Object); + + var result = directoryService.GetFilePaths(path); + fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(newPaths); + var secondResult = directoryService.GetFilePaths(path, true); + + Assert.Equal(cachedPaths, result); + Assert.Equal(newPaths, secondResult); + } + } +} diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index 6dec25aa4..c56ccb365 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -16,6 +16,7 @@ + From afe3b5999e00c7c705afe224e41755f5426e6b85 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 26 Mar 2021 17:40:55 -0600 Subject: [PATCH 199/402] Fix route naming --- Jellyfin.Api/Controllers/SubtitleController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index 7a44b26a4..1669a659d 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers /// The start position of the subtitle in ticks. /// File returned. /// A with the subtitle file. - [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{routeFormat}")] + [HttpGet("Videos/{routeItemId}/routeMediaSourceId/Subtitles/{routeIndex}/Stream.{routeFormat}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile("text/*")] public async Task GetSubtitle( From aae2aad0f25867f651c94a30d02a1893442d65da Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 27 Mar 2021 08:16:48 +0000 Subject: [PATCH 200/402] changed split to single quotes --- .../NetworkParseTests.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 5d22b4d33..39fba1fb2 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -201,29 +201,29 @@ namespace Jellyfin.Networking.Tests using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); // Test included. - Collection nc = nm.CreateIPCollection(settings.Split(","), false); + Collection nc = nm.CreateIPCollection(settings.Split(','), false); Assert.Equal(nc.AsString(), result1); // Test excluded. - nc = nm.CreateIPCollection(settings.Split(","), true); + nc = nm.CreateIPCollection(settings.Split(','), true); Assert.Equal(nc.AsString(), result3); conf.EnableIPV6 = false; nm.UpdateSettings(conf); // Test IP4 included. - nc = nm.CreateIPCollection(settings.Split(","), false); + nc = nm.CreateIPCollection(settings.Split(','), false); Assert.Equal(nc.AsString(), result2); // Test IP4 excluded. - nc = nm.CreateIPCollection(settings.Split(","), true); + nc = nm.CreateIPCollection(settings.Split(','), true); Assert.Equal(nc.AsString(), result4); conf.EnableIPV6 = true; nm.UpdateSettings(conf); // Test network addresses of collection. - nc = nm.CreateIPCollection(settings.Split(","), false); + nc = nm.CreateIPCollection(settings.Split(','), false); nc = nc.AsNetworks(); Assert.Equal(nc.AsString(), result5); } @@ -262,8 +262,8 @@ namespace Jellyfin.Networking.Tests using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - Collection nc1 = nm.CreateIPCollection(settings.Split(","), false); - Collection nc2 = nm.CreateIPCollection(compare.Split(","), false); + Collection nc1 = nm.CreateIPCollection(settings.Split(','), false); + Collection nc2 = nm.CreateIPCollection(compare.Split(','), false); Assert.Equal(nc1.Union(nc2).AsString(), result); } @@ -372,10 +372,10 @@ namespace Jellyfin.Networking.Tests using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); // Test included, IP6. - Collection ncSource = nm.CreateIPCollection(source.Split(",")); - Collection ncDest = nm.CreateIPCollection(dest.Split(",")); + Collection ncSource = nm.CreateIPCollection(source.Split(',')); + Collection ncDest = nm.CreateIPCollection(dest.Split(',')); Collection ncResult = ncSource.Union(ncDest); - Collection resultCollection = nm.CreateIPCollection(result.Split(",")); + Collection resultCollection = nm.CreateIPCollection(result.Split(',')); Assert.True(ncResult.Compare(resultCollection)); } @@ -527,7 +527,7 @@ namespace Jellyfin.Networking.Tests var conf = new NetworkConfiguration() { EnableIPV4 = true, - RemoteIPFilter = addresses.Split(","), + RemoteIPFilter = addresses.Split(','), IsRemoteIPFilterBlacklist = false }; using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); @@ -546,7 +546,7 @@ namespace Jellyfin.Networking.Tests var conf = new NetworkConfiguration() { EnableIPV4 = true, - RemoteIPFilter = addresses.Split(","), + RemoteIPFilter = addresses.Split(','), IsRemoteIPFilterBlacklist = true }; From 364e8931af70b97881638109858734f4ecaa0d0a Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sat, 27 Mar 2021 11:48:59 +0300 Subject: [PATCH 201/402] Check appropriate profile type --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bf33691c7..167fc984e 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -514,6 +514,8 @@ namespace MediaBrowser.Model.Dlna private static List GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable directPlayProfiles) { + var mediaType = videoStream != null ? DlnaProfileType.Video : DlnaProfileType.Audio; + var containerSupported = false; var audioSupported = false; var videoSupported = false; @@ -521,7 +523,7 @@ namespace MediaBrowser.Model.Dlna foreach (var profile in directPlayProfiles) { // Check container type - if (profile.SupportsContainer(item.Container)) + if (profile.Type == mediaType && profile.SupportsContainer(item.Container)) { containerSupported = true; From 81e3e5ca4883309b38377729e0bbd1b9f163ec7c Mon Sep 17 00:00:00 2001 From: Brian Arnold Date: Fri, 26 Mar 2021 10:02:23 -0400 Subject: [PATCH 202/402] Changed SessionController.SendMessageCommand implementation receive data in the POST body, as that is how the jellyfin-web client currently posts the data to the server. Resolves: #5628 --- Jellyfin.Api/Controllers/SessionController.cs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 0703d4255..ba68b4cbf 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -313,9 +313,7 @@ namespace Jellyfin.Api.Controllers /// Issues a command to a client to display a message to the user. /// /// The session id. - /// The message test. - /// The message header. - /// The message timeout. If omitted the user will have to confirm viewing the message. + /// The object containing Header, Message Text, and TimeoutMs. /// Message sent. /// A . [HttpPost("Sessions/{sessionId}/Message")] @@ -323,18 +321,25 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult SendMessageCommand( [FromRoute, Required] string sessionId, - [FromQuery, Required] string text, - [FromQuery] string? header, - [FromQuery] long? timeoutMs) + [FromBody] MessageCommand command) { - var command = new MessageCommand + if (command == null) { - Header = string.IsNullOrEmpty(header) ? "Message from Server" : header, - TimeoutMs = timeoutMs, - Text = text + throw new ArgumentException("Request body may not be null"); + } + //Need to check if message.Text is null, since [Required] can't be applied to properties of a deserialized object. + if (string.IsNullOrWhiteSpace(command.Text)) + { + throw new ArgumentNullException("Message Text may not be empty."); + } + var nullCorrectedCommand = new MessageCommand + { + Header = string.IsNullOrWhiteSpace(command.Header) ? "Message from Server" : command.Header, + TimeoutMs = command.TimeoutMs, + Text = command.Text }; - _sessionManager.SendMessageCommand(RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id, sessionId, command, CancellationToken.None); + _sessionManager.SendMessageCommand(RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id, sessionId, nullCorrectedCommand, CancellationToken.None); return NoContent(); } From f114ba57dd24ed38786a478df7a641f81dd9d5c7 Mon Sep 17 00:00:00 2001 From: Brian Arnold Date: Fri, 26 Mar 2021 10:37:59 -0400 Subject: [PATCH 203/402] Fixed comment and code block runes to match coding standards required by Jellyfin team. --- Jellyfin.Api/Controllers/SessionController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index ba68b4cbf..82aa32a7a 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -327,11 +327,13 @@ namespace Jellyfin.Api.Controllers { throw new ArgumentException("Request body may not be null"); } - //Need to check if message.Text is null, since [Required] can't be applied to properties of a deserialized object. + + // Need to check if message.Text is null, since [Required] can't be applied to properties of a deserialized object. if (string.IsNullOrWhiteSpace(command.Text)) { throw new ArgumentNullException("Message Text may not be empty."); } + var nullCorrectedCommand = new MessageCommand { Header = string.IsNullOrWhiteSpace(command.Header) ? "Message from Server" : command.Header, From 998833ea6e4c52f842949029b278008b276e3e6b Mon Sep 17 00:00:00 2001 From: "Brian C. Arnold" Date: Sat, 27 Mar 2021 23:28:08 -0400 Subject: [PATCH 204/402] Removed null check for body object and user [Required] attribute in replacement. --- Jellyfin.Api/Controllers/SessionController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 82aa32a7a..34dfb6ce7 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -321,12 +321,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult SendMessageCommand( [FromRoute, Required] string sessionId, - [FromBody] MessageCommand command) + [FromBody, Required] MessageCommand command) { - if (command == null) - { - throw new ArgumentException("Request body may not be null"); - } // Need to check if message.Text is null, since [Required] can't be applied to properties of a deserialized object. if (string.IsNullOrWhiteSpace(command.Text)) From 1669cb661858676e40f0e7f89205e4e93111da85 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 29 Mar 2021 10:35:29 +0200 Subject: [PATCH 205/402] Split valid and invalid tests --- .../Providers/ProviderIdParsers.cs | 4 +- .../Providers/ProviderIdParserTests.cs | 100 +++++++++++------- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs index 26eaccac5..64c2e1976 100644 --- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -22,12 +22,10 @@ namespace MediaBrowser.Common.Providers /// True if parsing was successful, false otherwise. public static bool TryFindImdbId(ReadOnlySpan text, [NotNullWhen(true)] out ReadOnlySpan imdbId) { - var tt = ImdbPrefix.AsSpan(); - // imdb id is at least 9 chars (tt + 7 numbers) while (text.Length >= 2 + ImdbMinNumbers) { - var ttPos = text.IndexOf(tt); + var ttPos = text.IndexOf(ImdbPrefix); if (ttPos == -1) { imdbId = default; diff --git a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs index 1ce54c59b..ef9d31cc1 100644 --- a/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs +++ b/tests/Jellyfin.Common.Tests/Providers/ProviderIdParserTests.cs @@ -7,59 +7,79 @@ namespace Jellyfin.Common.Tests.Providers public class ProviderIdParserTests { [Theory] - [InlineData("tt123456", false, null)] - [InlineData("tt1234567", true, "tt1234567")] - [InlineData("tt12345678", true, "tt12345678")] - [InlineData("https://www.imdb.com/title/tt123456", false, null)] - [InlineData("https://www.imdb.com/title/tt1234567", true, "tt1234567")] - [InlineData("https://www.imdb.com/title/tt12345678", true, "tt12345678")] - [InlineData(@"multiline\nhttps://www.imdb.com/title/tt1234567", true, "tt1234567")] - [InlineData(@"multiline\nhttps://www.imdb.com/title/tt12345678", true, "tt12345678")] - [InlineData("Jellyfin", false, null)] - [InlineData("tt1234567tt7654321", true, "tt1234567")] - [InlineData("tt12345678tt7654321", true, "tt12345678")] - [InlineData("tt123456789", true, "tt12345678")] - public void Parse_Imdb(string text, bool shouldSucceed, string? imdbId) + [InlineData("tt1234567", "tt1234567")] + [InlineData("tt12345678", "tt12345678")] + [InlineData("https://www.imdb.com/title/tt1234567", "tt1234567")] + [InlineData("https://www.imdb.com/title/tt12345678", "tt12345678")] + [InlineData(@"multiline\nhttps://www.imdb.com/title/tt1234567", "tt1234567")] + [InlineData(@"multiline\nhttps://www.imdb.com/title/tt12345678", "tt12345678")] + [InlineData("tt1234567tt7654321", "tt1234567")] + [InlineData("tt12345678tt7654321", "tt12345678")] + [InlineData("tt123456789", "tt12345678")] + public void FindImdbId_Valid_Success(string text, string expected) { - var succeeded = ProviderIdParsers.TryFindImdbId(text, out ReadOnlySpan parsedId); - Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(imdbId ?? Span.Empty.ToString(), parsedId.ToString()); + Assert.True(ProviderIdParsers.TryFindImdbId(text, out ReadOnlySpan parsedId)); + Assert.Equal(expected, parsedId.ToString()); } [Theory] - [InlineData("https://www.themoviedb.org/movie/30287-fallo", true, "30287")] - [InlineData("themoviedb.org/movie/30287", true, "30287")] - [InlineData("https://www.themoviedb.org/movie/fallo-30287", false, null)] - [InlineData("https://www.themoviedb.org/tv/1668-friends", false, null)] - public void Parse_TmdbMovie(string text, bool shouldSucceed, string? tmdbId) + [InlineData("tt123456")] + [InlineData("https://www.imdb.com/title/tt123456")] + [InlineData("Jellyfin")] + public void FindImdbId_Invalid_Success(string text) { - var succeeded = ProviderIdParsers.TryFindTmdbMovieId(text, out ReadOnlySpan parsedId); - Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(tmdbId ?? Span.Empty.ToString(), parsedId.ToString()); + Assert.False(ProviderIdParsers.TryFindImdbId(text, out _)); } [Theory] - [InlineData("https://www.themoviedb.org/tv/1668-friends", true, "1668")] - [InlineData("themoviedb.org/tv/1668", true, "1668")] - [InlineData("https://www.themoviedb.org/tv/friends-1668", false, null)] - [InlineData("https://www.themoviedb.org/movie/30287-fallo", false, null)] - public void Parse_TmdbSeries(string text, bool shouldSucceed, string? tmdbId) + [InlineData("https://www.themoviedb.org/movie/30287-fallo", "30287")] + [InlineData("themoviedb.org/movie/30287", "30287")] + public void FindTmdbMovieId_Valid_Success(string text, string expected) { - var succeeded = ProviderIdParsers.TryFindTmdbSeriesId(text, out ReadOnlySpan parsedId); - Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(tmdbId ?? Span.Empty.ToString(), parsedId.ToString()); + Assert.True(ProviderIdParsers.TryFindTmdbMovieId(text, out ReadOnlySpan parsedId)); + Assert.Equal(expected, parsedId.ToString()); } [Theory] - [InlineData("https://www.thetvdb.com/?tab=series&id=121361", true, "121361")] - [InlineData("thetvdb.com/?tab=series&id=121361", true, "121361")] - [InlineData("thetvdb.com/?tab=series&id=Jellyfin121361", false, null)] - [InlineData("https://www.themoviedb.org/tv/1668-friends", false, null)] - public void Parse_Tvdb(string text, bool shouldSucceed, string? tvdbId) + [InlineData("https://www.themoviedb.org/movie/fallo-30287")] + [InlineData("https://www.themoviedb.org/tv/1668-friends")] + public void FindTmdbMovieId_Invalid_Success(string text) { - var succeeded = ProviderIdParsers.TryFindTvdbId(text, out ReadOnlySpan parsedId); - Assert.Equal(shouldSucceed, succeeded); - Assert.Equal(tvdbId ?? Span.Empty.ToString(), parsedId.ToString()); + Assert.False(ProviderIdParsers.TryFindTmdbMovieId(text, out _)); + } + + [Theory] + [InlineData("https://www.themoviedb.org/tv/1668-friends", "1668")] + [InlineData("themoviedb.org/tv/1668", "1668")] + public void FindTmdbSeriesId_Valid_Success(string text, string expected) + { + Assert.True(ProviderIdParsers.TryFindTmdbSeriesId(text, out ReadOnlySpan parsedId)); + Assert.Equal(expected, parsedId.ToString()); + } + + [Theory] + [InlineData("https://www.themoviedb.org/tv/friends-1668")] + [InlineData("https://www.themoviedb.org/movie/30287-fallo")] + public void FindTmdbSeriesId_Invalid_Success(string text) + { + Assert.False(ProviderIdParsers.TryFindTmdbSeriesId(text, out _)); + } + + [Theory] + [InlineData("https://www.thetvdb.com/?tab=series&id=121361", "121361")] + [InlineData("thetvdb.com/?tab=series&id=121361", "121361")] + public void FindTvdbId_Valid_Success(string text, string expected) + { + Assert.True(ProviderIdParsers.TryFindTvdbId(text, out ReadOnlySpan parsedId)); + Assert.Equal(expected, parsedId.ToString()); + } + + [Theory] + [InlineData("thetvdb.com/?tab=series&id=Jellyfin121361")] + [InlineData("https://www.themoviedb.org/tv/1668-friends")] + public void FindTvdbId_Invalid_Success(string text) + { + Assert.False(ProviderIdParsers.TryFindTvdbId(text, out _)); } } } From 023467ebcee2ada86ba99d2dccb00141ab0ed264 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Mar 2021 12:00:44 +0000 Subject: [PATCH 206/402] Bump Serilog.AspNetCore from 3.4.0 to 4.1.0 Bumps [Serilog.AspNetCore](https://github.com/serilog/serilog-aspnetcore) from 3.4.0 to 4.1.0. - [Release notes](https://github.com/serilog/serilog-aspnetcore/releases) - [Commits](https://github.com/serilog/serilog-aspnetcore/compare/v3.4.0...v4.1.0) Signed-off-by: dependabot[bot] --- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 09799307b..b7406d849 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -41,7 +41,7 @@ - + From 949ef2ed10bd79387fe3e2e1ff34af98c89ee3b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Mar 2021 12:00:57 +0000 Subject: [PATCH 207/402] Bump BlurHashSharp from 1.1.1 to 1.2.0 Bumps [BlurHashSharp](https://github.com/Bond-009/BlurHashSharp) from 1.1.1 to 1.2.0. - [Release notes](https://github.com/Bond-009/BlurHashSharp/releases) - [Commits](https://github.com/Bond-009/BlurHashSharp/commits/v1.2.0) Signed-off-by: dependabot[bot] --- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 3fc44640b..5f9c4d679 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -20,7 +20,7 @@ - + From ca25301e649342ef5598822fdd9ffc6eb2cd5065 Mon Sep 17 00:00:00 2001 From: Brian Arnold Date: Mon, 29 Mar 2021 10:10:44 -0400 Subject: [PATCH 208/402] Added Required attribute to Text property of MessageCommand. --- Jellyfin.Api/Controllers/SessionController.cs | 7 ------- MediaBrowser.Model/Session/MessageCommand.cs | 4 +++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 34dfb6ce7..14686222b 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -323,13 +323,6 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromBody, Required] MessageCommand command) { - - // Need to check if message.Text is null, since [Required] can't be applied to properties of a deserialized object. - if (string.IsNullOrWhiteSpace(command.Text)) - { - throw new ArgumentNullException("Message Text may not be empty."); - } - var nullCorrectedCommand = new MessageCommand { Header = string.IsNullOrWhiteSpace(command.Header) ? "Message from Server" : command.Header, diff --git a/MediaBrowser.Model/Session/MessageCommand.cs b/MediaBrowser.Model/Session/MessageCommand.cs index 09abfbb3f..0ee3c720e 100644 --- a/MediaBrowser.Model/Session/MessageCommand.cs +++ b/MediaBrowser.Model/Session/MessageCommand.cs @@ -1,12 +1,14 @@ #nullable disable #pragma warning disable CS1591 +using System.ComponentModel.DataAnnotations; + namespace MediaBrowser.Model.Session { public class MessageCommand { public string Header { get; set; } - + [Required(AllowEmptyStrings = false)] public string Text { get; set; } public long? TimeoutMs { get; set; } From 54107ae88262d835cac2d5a6f335b0c10d050b1a Mon Sep 17 00:00:00 2001 From: Brian Arnold Date: Mon, 29 Mar 2021 11:40:07 -0400 Subject: [PATCH 209/402] Fix spacing requirement for MessageCommand. --- MediaBrowser.Model/Session/MessageCommand.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Model/Session/MessageCommand.cs b/MediaBrowser.Model/Session/MessageCommand.cs index 0ee3c720e..cc9db8e6c 100644 --- a/MediaBrowser.Model/Session/MessageCommand.cs +++ b/MediaBrowser.Model/Session/MessageCommand.cs @@ -8,6 +8,7 @@ namespace MediaBrowser.Model.Session public class MessageCommand { public string Header { get; set; } + [Required(AllowEmptyStrings = false)] public string Text { get; set; } From 80fe48fda972eff0d6b58ac8a9e2f63d44a1b23e Mon Sep 17 00:00:00 2001 From: BrianCArnold Date: Tue, 30 Mar 2021 07:29:40 -0400 Subject: [PATCH 210/402] Update SessionController.cs Removed unnecessary construction of POCO to represent data from POST body --- Jellyfin.Api/Controllers/SessionController.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 14686222b..b64cbe30c 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -323,12 +323,10 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromBody, Required] MessageCommand command) { - var nullCorrectedCommand = new MessageCommand + if (string.IsNullOrWhiteSpace(command.Header)) { - Header = string.IsNullOrWhiteSpace(command.Header) ? "Message from Server" : command.Header, - TimeoutMs = command.TimeoutMs, - Text = command.Text - }; + command.Header = "Message from Server"; + } _sessionManager.SendMessageCommand(RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id, sessionId, nullCorrectedCommand, CancellationToken.None); From 8c6bd2537c47dafab46ed9ca756b9f91f995603c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Tue, 30 Mar 2021 15:15:16 +0200 Subject: [PATCH 211/402] Return Major.Minor.Build instead of Major.Minor.Build.Revision for OpenAPI version --- Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index d26ac251c..2b34370a0 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -260,7 +260,7 @@ namespace Jellyfin.Server.Extensions { return serviceCollection.AddSwaggerGen(c => { - var version = typeof(ApplicationHost).Assembly.GetName().Version?.ToString() ?? "0.0.0.1"; + var version = typeof(ApplicationHost).Assembly.GetName().Version?.ToString(3) ?? "0.0.1"; c.SwaggerDoc("api-docs", new OpenApiInfo { Title = "Jellyfin API", From af03b280bc91f65712af2cc854e8bf1ad349748a Mon Sep 17 00:00:00 2001 From: BrianCArnold Date: Wed, 31 Mar 2021 01:23:51 -0400 Subject: [PATCH 212/402] Update SessionController.cs --- Jellyfin.Api/Controllers/SessionController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index b64cbe30c..1981e213e 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -323,7 +323,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromBody, Required] MessageCommand command) { - if (string.IsNullOrWhiteSpace(command.Header)) + if (string.IsNullOrWhiteSpace(command.Header)) { command.Header = "Message from Server"; } From 4fa2a32d81e263ef51140e2ae8259a02fdfb0d7d Mon Sep 17 00:00:00 2001 From: BrianCArnold Date: Wed, 31 Mar 2021 01:24:38 -0400 Subject: [PATCH 213/402] Apply suggestions from code review Co-authored-by: Claus Vium --- Jellyfin.Api/Controllers/SessionController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 1981e213e..7bd0b6918 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -325,10 +325,10 @@ namespace Jellyfin.Api.Controllers { if (string.IsNullOrWhiteSpace(command.Header)) { - command.Header = "Message from Server"; + command.Header = "Message from Server"; } - _sessionManager.SendMessageCommand(RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id, sessionId, nullCorrectedCommand, CancellationToken.None); + _sessionManager.SendMessageCommand(RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id, sessionId, command, CancellationToken.None); return NoContent(); } From 28a0eb6d53b472fd62d4cc6001770e0cabdf38cd Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 1 Apr 2021 14:28:03 +0200 Subject: [PATCH 214/402] set original title in tmdbmovieprovider --- MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 4963777bc..833d1ae38 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -175,6 +175,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var movie = new Movie { Name = movieResult.Title ?? movieResult.OriginalTitle, + OriginalTitle = movieResult.OriginalTitle, Overview = movieResult.Overview?.Replace("\n\n", "\n", StringComparison.InvariantCulture), Tagline = movieResult.Tagline, ProductionLocations = movieResult.ProductionCountries.Select(pc => pc.Name).ToArray() From df60d176b8105b36e186191bef7dc6811ce5dbcd Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Thu, 1 Apr 2021 15:27:28 +0200 Subject: [PATCH 215/402] ensure only valid images are saved in ItemImageProvider --- MediaBrowser.Providers/Manager/ItemImageProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index ffc6889fa..4471a25b2 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -469,6 +469,7 @@ namespace MediaBrowser.Providers.Manager try { using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); await _providerManager.SaveImage( From 11f7ab4dd4deec133a18e2f77e1d3a6dff2b9f3e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 1 Apr 2021 19:13:03 +0200 Subject: [PATCH 216/402] Add tests for CopyToExtensions --- .../Extensions/CopyToExtensionsTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs diff --git a/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs b/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs new file mode 100644 index 000000000..9903409fa --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Extensions; +using Xunit; + +namespace Jellyfin.Common.Tests.Extensions +{ + public static class CopyToExtensionsTests + { + public static IEnumerable CopyTo_Valid_Correct_TestData() + { + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 0, new[] { 0, 1, 2, 3, 4, 5 } }; + yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 2, new[] { 5, 4, 0, 1, 2, 0 } }; + } + + [Theory] + [MemberData(nameof(CopyTo_Valid_Correct_TestData))] + public static void CopyTo_Valid_Correct(IReadOnlyList source, IList destination, int index, IList expected) + { + source.CopyTo(destination, index); + Assert.Equal(expected, destination); + } + + public static IEnumerable CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData() + { + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, -1 }; + yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 6 }; + yield return new object[] { new[] { 0, 1, 2 }, Array.Empty(), 0 }; + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0 }, 0 }; + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 1 }; + } + + [Theory] + [MemberData(nameof(CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData))] + public static void CopyTo_Invalid_ThrowsArgumentOutOfRangeException(IReadOnlyList source, IList destination, int index) + { + Assert.Throws(() => source.CopyTo(destination, index)); + } + } +} From aa769573387b051fb79246885be8eb5e8cf97e76 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 1 Apr 2021 19:16:00 +0200 Subject: [PATCH 217/402] Remove useless code --- Emby.Dlna/ConnectionManager/ControlHandler.cs | 2 +- Emby.Dlna/ContentDirectory/ControlHandler.cs | 45 ++++++------------- .../MediaReceiverRegistrar/ControlHandler.cs | 2 +- Emby.Dlna/Service/BaseControlHandler.cs | 2 +- .../Library/Resolvers/TV/SeriesResolver.cs | 22 +-------- .../LiveTv/EmbyTV/EpgChannelData.cs | 43 +++++++----------- 6 files changed, 35 insertions(+), 81 deletions(-) diff --git a/Emby.Dlna/ConnectionManager/ControlHandler.cs b/Emby.Dlna/ConnectionManager/ControlHandler.cs index 2f8d197a7..1a1790ee6 100644 --- a/Emby.Dlna/ConnectionManager/ControlHandler.cs +++ b/Emby.Dlna/ConnectionManager/ControlHandler.cs @@ -31,7 +31,7 @@ namespace Emby.Dlna.ConnectionManager } /// - protected override void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter) + protected override void WriteResult(string methodName, IReadOnlyDictionary methodParams, XmlWriter xmlWriter) { if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase)) { diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 27f1fdaba..713f95099 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -121,7 +121,7 @@ namespace Emby.Dlna.ContentDirectory } /// - protected override void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter) + protected override void WriteResult(string methodName, IReadOnlyDictionary methodParams, XmlWriter xmlWriter) { if (xmlWriter == null) { @@ -201,8 +201,8 @@ namespace Emby.Dlna.ContentDirectory /// /// Adds a "XSetBookmark" element to the xml document. /// - /// The . - private void HandleXSetBookmark(IDictionary sparams) + /// The method parameters. + private void HandleXSetBookmark(IReadOnlyDictionary sparams) { var id = sparams["ObjectID"]; @@ -305,35 +305,18 @@ namespace Emby.Dlna.ContentDirectory return builder.ToString(); } - /// - /// Returns the value in the key of the dictionary, or defaultValue if it doesn't exist. - /// - /// The . - /// The key. - /// The defaultValue. - /// The . - public static string GetValueOrDefault(IDictionary sparams, string key, string defaultValue) - { - if (sparams != null && sparams.TryGetValue(key, out string val)) - { - return val; - } - - return defaultValue; - } - /// /// Builds the "Browse" xml response. /// /// The . - /// The . + /// The method parameters. /// The device Id to use. - private void HandleBrowse(XmlWriter xmlWriter, IDictionary sparams, string deviceId) + private void HandleBrowse(XmlWriter xmlWriter, IReadOnlyDictionary sparams, string deviceId) { var id = sparams["ObjectID"]; var flag = sparams["BrowseFlag"]; - var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*")); - var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty)); + var filter = new Filter(sparams.GetValueOrDefault("Filter", "*")); + var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", string.Empty)); var provided = 0; @@ -435,9 +418,9 @@ namespace Emby.Dlna.ContentDirectory /// Builds the response to the "X_BrowseByLetter request. /// /// The . - /// The . + /// The method parameters. /// The device id. - private void HandleXBrowseByLetter(XmlWriter xmlWriter, IDictionary sparams, string deviceId) + private void HandleXBrowseByLetter(XmlWriter xmlWriter, IReadOnlyDictionary sparams, string deviceId) { // TODO: Implement this method HandleSearch(xmlWriter, sparams, deviceId); @@ -447,13 +430,13 @@ namespace Emby.Dlna.ContentDirectory /// Builds a response to the "Search" request. /// /// The xmlWriter. - /// The sparams. + /// The method parameters. /// The deviceId. - private void HandleSearch(XmlWriter xmlWriter, IDictionary sparams, string deviceId) + private void HandleSearch(XmlWriter xmlWriter, IReadOnlyDictionary sparams, string deviceId) { - var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", string.Empty)); - var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty)); - var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*")); + var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", string.Empty)); + var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", string.Empty)); + var filter = new Filter(sparams.GetValueOrDefault("Filter", "*")); // sort example: dc:title, dc:date diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs index 464f71a6f..d8fb12742 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs @@ -24,7 +24,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar } /// - protected override void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter) + protected override void WriteResult(string methodName, IReadOnlyDictionary methodParams, XmlWriter xmlWriter) { if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase)) { diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 8d2486fee..fda8346f9 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -210,7 +210,7 @@ namespace Emby.Dlna.Service } } - protected abstract void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter); + protected abstract void WriteResult(string methodName, IReadOnlyDictionary methodParams, XmlWriter xmlWriter); private void LogRequest(ControlRequest request) { diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 732bfd94d..e8b5f4fe7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -127,7 +127,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV { if (child.IsDirectory) { - if (IsSeasonFolder(child.FullName, isTvContentType, libraryManager)) + if (IsSeasonFolder(child.FullName, isTvContentType)) { logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName); return true; @@ -160,24 +160,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return false; } - /// - /// Determines whether [is place holder] [the specified path]. - /// - /// The path. - /// true if [is place holder] [the specified path]; otherwise, false. - /// path - private static bool IsVideoPlaceHolder(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(path)); - } - - var extension = Path.GetExtension(path); - - return string.Equals(extension, ".disc", StringComparison.OrdinalIgnoreCase); - } - /// /// Determines whether [is season folder] [the specified path]. /// @@ -185,7 +167,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// if set to true [is tv content type]. /// The library manager. /// true if [is season folder] [the specified path]; otherwise, false. - private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager) + private static bool IsSeasonFolder(string path, bool isTvContentType) { var seasonNumber = SeasonPathParser.Parse(path, isTvContentType, isTvContentType).SeasonNumber; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs index 463d0ed0a..8c27ca76e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs @@ -9,55 +9,44 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV internal class EpgChannelData { + + private readonly Dictionary _channelsById; + + private readonly Dictionary _channelsByNumber; + + private readonly Dictionary _channelsByName; + public EpgChannelData(IEnumerable channels) { - ChannelsById = new Dictionary(StringComparer.OrdinalIgnoreCase); - ChannelsByNumber = new Dictionary(StringComparer.OrdinalIgnoreCase); - ChannelsByName = new Dictionary(StringComparer.OrdinalIgnoreCase); + _channelsById = new Dictionary(StringComparer.OrdinalIgnoreCase); + _channelsByNumber = new Dictionary(StringComparer.OrdinalIgnoreCase); + _channelsByName = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var channel in channels) { - ChannelsById[channel.Id] = channel; + _channelsById[channel.Id] = channel; if (!string.IsNullOrEmpty(channel.Number)) { - ChannelsByNumber[channel.Number] = channel; + _channelsByNumber[channel.Number] = channel; } var normalizedName = NormalizeName(channel.Name ?? string.Empty); if (!string.IsNullOrWhiteSpace(normalizedName)) { - ChannelsByName[normalizedName] = channel; + _channelsByName[normalizedName] = channel; } } } - private Dictionary ChannelsById { get; set; } - - private Dictionary ChannelsByNumber { get; set; } - - private Dictionary ChannelsByName { get; set; } - public ChannelInfo GetChannelById(string id) - { - ChannelsById.TryGetValue(id, out var result); - - return result; - } + => _channelsById.GetValueOrDefault(id); public ChannelInfo GetChannelByNumber(string number) - { - ChannelsByNumber.TryGetValue(number, out var result); - - return result; - } + => _channelsByNumber.GetValueOrDefault(number); public ChannelInfo GetChannelByName(string name) - { - ChannelsByName.TryGetValue(name, out var result); - - return result; - } + => _channelsByName.GetValueOrDefault(name); public static string NormalizeName(string value) { From 21e7ceae8e15e19bdb111e51aba41e6a3ac3333d Mon Sep 17 00:00:00 2001 From: Max Rumpf Date: Thu, 1 Apr 2021 19:18:14 +0200 Subject: [PATCH 218/402] StreamBuilder tweaks (#5668) Co-authored-by: Cody Robibero --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bf33691c7..8299059a5 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -674,7 +674,7 @@ namespace MediaBrowser.Model.Dlna var videoStream = item.VideoStream; - // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough + // TODO: This doesn't account for situations where the device is able to handle the media's bitrate, but the connection isn't fast enough var directPlayEligibilityResult = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true) ?? 0, subtitleStream, options, PlayMethod.DirectPlay); var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, subtitleStream, options, PlayMethod.DirectStream); bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult.Item1); @@ -1017,14 +1017,15 @@ namespace MediaBrowser.Model.Dlna } DeviceProfile profile = options.Profile; + string container = mediaSource.Container; // See if it can be direct played DirectPlayProfile directPlay = null; - foreach (var i in profile.DirectPlayProfiles) + foreach (var p in profile.DirectPlayProfiles) { - if (i.Type == DlnaProfileType.Video && IsVideoDirectPlaySupported(i, mediaSource, videoStream, audioStream)) + if (p.Type == DlnaProfileType.Video && IsVideoDirectPlaySupported(p, container, videoStream, audioStream)) { - directPlay = i; + directPlay = p; break; } } @@ -1032,23 +1033,23 @@ namespace MediaBrowser.Model.Dlna if (directPlay == null) { _logger.LogInformation( - "Profile: {0}, No video direct play profiles found for {1} with codec {2}", - profile?.Name ?? "Unknown Profile", - mediaSource?.Path ?? "Unknown path", - videoStream?.Codec ?? "Unknown codec"); + "Container: {Container}, Video: {Video}, Audio: {Audio} cannot be direct played by profile: {Profile} for path: {Path}", + container, + videoStream?.Codec ?? "no video", + audioStream?.Codec ?? "no audio", + profile.Name ?? "unknown profile", + mediaSource.Path ?? "unknown path"); return (null, GetTranscodeReasonsFromDirectPlayProfile(mediaSource, videoStream, audioStream, profile.DirectPlayProfiles)); } - string container = mediaSource.Container; - var conditions = new List(); - foreach (var i in profile.ContainerProfiles) + foreach (var p in profile.ContainerProfiles) { - if (i.Type == DlnaProfileType.Video - && i.ContainsContainer(container)) + if (p.Type == DlnaProfileType.Video + && p.ContainsContainer(container)) { - foreach (var c in i.Conditions) + foreach (var c in p.Conditions) { conditions.Add(c); } @@ -1896,10 +1897,10 @@ namespace MediaBrowser.Model.Dlna return true; } - private bool IsVideoDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream) + private bool IsVideoDirectPlaySupported(DirectPlayProfile profile, string container, MediaStream videoStream, MediaStream audioStream) { // Check container type - if (!profile.SupportsContainer(item.Container)) + if (!profile.SupportsContainer(container)) { return false; } From c533b204961b8fe19272b31b5fb5473be0eb801b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 1 Apr 2021 19:39:00 +0200 Subject: [PATCH 219/402] Remove ManagedFileSystem.IsRootPath `Path.IsPathRooted` should be used instead --- .../IO/ManagedFileSystem.cs | 32 ++++++------------- MediaBrowser.Model/IO/IFileSystem.cs | 7 ---- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index c0e757543..679795dd2 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -55,7 +55,7 @@ namespace Emby.Server.Implementations.IO } var extension = Path.GetExtension(filename); - return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase)); + return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); } /// @@ -487,26 +487,9 @@ namespace Emby.Server.Implementations.IO throw new ArgumentNullException(nameof(path)); } - var separatorChar = Path.DirectorySeparatorChar; - - return path.IndexOf(parentPath.TrimEnd(separatorChar) + separatorChar, StringComparison.OrdinalIgnoreCase) != -1; - } - - public virtual bool IsRootPath(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(path)); - } - - var parent = Path.GetDirectoryName(path); - - if (!string.IsNullOrEmpty(parent)) - { - return false; - } - - return true; + return path.Contains( + Path.TrimEndingDirectorySeparator(parentPath) + Path.DirectorySeparatorChar, + _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } public virtual string NormalizePath(string path) @@ -521,7 +504,7 @@ namespace Emby.Server.Implementations.IO return path; } - return path.TrimEnd(Path.DirectorySeparatorChar); + return Path.TrimEndingDirectorySeparator(path); } public virtual bool AreEqual(string path1, string path2) @@ -536,7 +519,10 @@ namespace Emby.Server.Implementations.IO return false; } - return string.Equals(NormalizePath(path1), NormalizePath(path2), StringComparison.OrdinalIgnoreCase); + return string.Equals( + NormalizePath(path1), + NormalizePath(path2), + _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } public virtual string GetFileNameWithoutExtension(FileSystemMetadata info) diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index ef08ecec6..e5c26430a 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -117,13 +117,6 @@ namespace MediaBrowser.Model.IO /// true if [contains sub path] [the specified parent path]; otherwise, false. bool ContainsSubPath(string parentPath, string path); - /// - /// Determines whether [is root path] [the specified path]. - /// - /// The path. - /// true if [is root path] [the specified path]; otherwise, false. - bool IsRootPath(string path); - /// /// Normalizes the path. /// From a0258618acc66b80e9f5cdd535a31baf12857fb0 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 1 Apr 2021 21:24:34 +0200 Subject: [PATCH 220/402] Update Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs Co-authored-by: Claus Vium --- .../Library/Resolvers/TV/SeriesResolver.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index e8b5f4fe7..4a9d2cf8c 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -165,7 +165,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// /// The path. /// if set to true [is tv content type]. - /// The library manager. /// true if [is season folder] [the specified path]; otherwise, false. private static bool IsSeasonFolder(string path, bool isTvContentType) { From d9a50cb510cfe7b5a97def22fe3bc090cafc0638 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Fri, 2 Apr 2021 19:06:38 +0100 Subject: [PATCH 221/402] Various DLNA Optimizations --- Emby.Dlna/Didl/DidlBuilder.cs | 9 +- Emby.Dlna/PlayTo/PlayToController.cs | 8 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 5 +- .../Dlna/ContentFeatureBuilder.cs | 85 +++++++++---------- .../Dlna/MediaFormatProfileResolver.cs | 37 ++++---- 5 files changed, 68 insertions(+), 76 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 8b50d47fb..66ae07329 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -208,7 +208,8 @@ namespace Emby.Dlna.Didl var targetWidth = streamInfo.TargetWidth; var targetHeight = streamInfo.TargetHeight; - var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader( + var contentFeatureList = ContentFeatureBuilder.BuildVideoHeader( + _profile, streamInfo.Container, streamInfo.TargetVideoCodec.FirstOrDefault(), streamInfo.TargetAudioCodec.FirstOrDefault(), @@ -599,7 +600,8 @@ namespace Emby.Dlna.Didl ? MimeTypes.GetMimeType(filename) : mediaProfile.MimeType; - var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader( + var contentFeatures = ContentFeatureBuilder.BuildAudioHeader( + _profile, streamInfo.Container, streamInfo.TargetAudioCodec.FirstOrDefault(), targetAudioBitrate, @@ -1033,8 +1035,7 @@ namespace Emby.Dlna.Didl var width = albumartUrlInfo.width ?? maxWidth; var height = albumartUrlInfo.height ?? maxHeight; - var contentFeatures = new ContentFeatureBuilder(_profile) - .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn); + var contentFeatures = ContentFeatureBuilder.BuildImageHeader(_profile, format, width, height, imageInfo.IsDirectStream, org_Pn); writer.WriteAttributeString( "protocolInfo", diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index e4923b9eb..c6fdcd8ea 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -499,8 +499,8 @@ namespace Emby.Dlna.PlayTo if (streamInfo.MediaType == DlnaProfileType.Audio) { - return new ContentFeatureBuilder(profile) - .BuildAudioHeader( + return ContentFeatureBuilder.BuildAudioHeader( + profile, streamInfo.Container, streamInfo.TargetAudioCodec.FirstOrDefault(), streamInfo.TargetAudioBitrate, @@ -514,8 +514,8 @@ namespace Emby.Dlna.PlayTo if (streamInfo.MediaType == DlnaProfileType.Video) { - var list = new ContentFeatureBuilder(profile) - .BuildVideoHeader( + var list = ContentFeatureBuilder.BuildVideoHeader( + profile, streamInfo.Container, streamInfo.TargetVideoCodec.FirstOrDefault(), streamInfo.TargetAudioCodec.FirstOrDefault(), diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index d20a02cf5..23f90052a 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -306,7 +306,8 @@ namespace Jellyfin.Api.Helpers if (!state.IsVideoRequest) { - responseHeaders.Add("contentFeatures.dlna.org", new ContentFeatureBuilder(profile).BuildAudioHeader( + responseHeaders.Add("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader( + profile, state.OutputContainer, audioCodec, state.OutputAudioBitrate, @@ -323,7 +324,7 @@ namespace Jellyfin.Api.Helpers responseHeaders.Add( "contentFeatures.dlna.org", - new ContentFeatureBuilder(profile).BuildVideoHeader(state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty); + ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty); } } diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index ec106f105..600a44157 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -10,14 +10,8 @@ namespace MediaBrowser.Model.Dlna { public class ContentFeatureBuilder { - private readonly DeviceProfile _profile; - - public ContentFeatureBuilder(DeviceProfile profile) - { - _profile = profile; - } - - public string BuildImageHeader( + public static string BuildImageHeader( + DeviceProfile profile, string container, int? width, int? height, @@ -38,27 +32,31 @@ namespace MediaBrowser.Model.Dlna ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); - ResponseProfile mediaProfile = _profile.GetImageMediaProfile( - container, - width, - height); - if (string.IsNullOrEmpty(orgPn)) { + ResponseProfile mediaProfile = profile.GetImageMediaProfile( + container, + width, + height); + orgPn = mediaProfile?.OrgPn; + + if (string.IsNullOrEmpty(orgPn)) + { + orgPn = GetImageOrgPnValue(container, width, height); + } } if (string.IsNullOrEmpty(orgPn)) { - orgPn = GetImageOrgPnValue(container, width, height); + return orgOp.TrimStart(';') + orgCi + dlnaflags; } - string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn; - - return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + return "DLNA.ORG_PN=" + orgPn + orgOp + orgCi + dlnaflags; } - public string BuildAudioHeader( + public static string BuildAudioHeader( + DeviceProfile profile, string container, string audioCodec, int? audioBitrate, @@ -94,7 +92,7 @@ namespace MediaBrowser.Model.Dlna ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); - ResponseProfile mediaProfile = _profile.GetAudioMediaProfile( + ResponseProfile mediaProfile = profile.GetAudioMediaProfile( container, audioCodec, audioChannels, @@ -109,12 +107,16 @@ namespace MediaBrowser.Model.Dlna orgPn = GetAudioOrgPnValue(container, audioBitrate, audioSampleRate, audioChannels); } - string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn; + if (string.IsNullOrEmpty(orgPn)) + { + return orgOp.TrimStart(';') + orgCi + dlnaflags; + } - return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + return "DLNA.ORG_PN=" + orgPn + orgOp + orgCi + dlnaflags; } - public List BuildVideoHeader( + public static List BuildVideoHeader( + DeviceProfile profile, string container, string videoCodec, string audioCodec, @@ -163,7 +165,7 @@ namespace MediaBrowser.Model.Dlna ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); - ResponseProfile mediaProfile = _profile.GetVideoMediaProfile( + ResponseProfile mediaProfile = profile.GetVideoMediaProfile( container, audioCodec, videoCodec, @@ -192,9 +194,9 @@ namespace MediaBrowser.Model.Dlna } else { - foreach (string s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp)) + foreach (var s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp)) { - orgPnValues.Add(s); + orgPnValues.Add(s.ToString()); break; } } @@ -203,20 +205,20 @@ namespace MediaBrowser.Model.Dlna foreach (string orgPn in orgPnValues) { - string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn; - - var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); - - contentFeatureList.Add(value); + if (string.IsNullOrEmpty(orgPn)) + { + contentFeatureList.Add(orgOp.TrimStart(';') + orgCi + dlnaflags); + continue; + } + else + { + contentFeatureList.Add("DLNA.ORG_PN=" + orgPn + orgCi + dlnaflags); + } } if (orgPnValues.Count == 0) { - string contentFeatures = string.Empty; - - var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); - - contentFeatureList.Add(value); + contentFeatureList.Add(orgOp.TrimStart(';') + orgCi + dlnaflags); } return contentFeatureList; @@ -224,19 +226,14 @@ namespace MediaBrowser.Model.Dlna private static string GetImageOrgPnValue(string container, int? width, int? height) { - MediaFormatProfile? format = new MediaFormatProfileResolver() - .ResolveImageFormat( - container, - width, - height); + MediaFormatProfile? format = MediaFormatProfileResolver.ResolveImageFormat(container, width, height); return format.HasValue ? format.Value.ToString() : null; } private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels) { - MediaFormatProfile? format = new MediaFormatProfileResolver() - .ResolveAudioFormat( + MediaFormatProfile? format = MediaFormatProfileResolver.ResolveAudioFormat( container, audioBitrate, audioSampleRate, @@ -245,9 +242,9 @@ namespace MediaBrowser.Model.Dlna return format.HasValue ? format.Value.ToString() : null; } - private static string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp) + private static MediaFormatProfile[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp) { - return new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp); + return MediaFormatProfileResolver.ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp); } } } diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs index f61b8d59e..7ce248509 100644 --- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs +++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs @@ -9,16 +9,9 @@ using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna { - public class MediaFormatProfileResolver + public static class MediaFormatProfileResolver { - public string[] ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) - { - return ResolveVideoFormatInternal(container, videoCodec, audioCodec, width, height, timestampType) - .Select(i => i.ToString()) - .ToArray(); - } - - private MediaFormatProfile[] ResolveVideoFormatInternal(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) + public static MediaFormatProfile[] ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) { if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase)) { @@ -84,7 +77,7 @@ namespace MediaBrowser.Model.Dlna return Array.Empty(); } - private MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) + private static MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) { string suffix = string.Empty; @@ -209,12 +202,12 @@ namespace MediaBrowser.Model.Dlna return Array.Empty(); } - private MediaFormatProfile ValueOf(string value) + private static MediaFormatProfile ValueOf(string value) { return (MediaFormatProfile)Enum.Parse(typeof(MediaFormatProfile), value, true); } - private MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height) + private static MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height) { if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { @@ -287,7 +280,7 @@ namespace MediaBrowser.Model.Dlna return null; } - private MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec) + private static MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec) { if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { @@ -317,7 +310,7 @@ namespace MediaBrowser.Model.Dlna return null; } - private MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height) + private static MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height) { if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase) && (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "wmapro", StringComparison.OrdinalIgnoreCase))) @@ -371,7 +364,7 @@ namespace MediaBrowser.Model.Dlna return null; } - public MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels) + public static MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels) { if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase)) { @@ -413,7 +406,7 @@ namespace MediaBrowser.Model.Dlna return null; } - private MediaFormatProfile ResolveAudioASFFormat(int? bitrate) + private static MediaFormatProfile ResolveAudioASFFormat(int? bitrate) { if (bitrate.HasValue && bitrate.Value <= 193) { @@ -423,7 +416,7 @@ namespace MediaBrowser.Model.Dlna return MediaFormatProfile.WMA_FULL; } - private MediaFormatProfile? ResolveAudioLPCMFormat(int? frequency, int? channels) + private static MediaFormatProfile? ResolveAudioLPCMFormat(int? frequency, int? channels) { if (frequency.HasValue && channels.HasValue) { @@ -453,7 +446,7 @@ namespace MediaBrowser.Model.Dlna return MediaFormatProfile.LPCM16_48_STEREO; } - private MediaFormatProfile ResolveAudioMP4Format(int? bitrate) + private static MediaFormatProfile ResolveAudioMP4Format(int? bitrate) { if (bitrate.HasValue && bitrate.Value <= 320) { @@ -463,7 +456,7 @@ namespace MediaBrowser.Model.Dlna return MediaFormatProfile.AAC_ISO; } - private MediaFormatProfile ResolveAudioADTSFormat(int? bitrate) + private static MediaFormatProfile ResolveAudioADTSFormat(int? bitrate) { if (bitrate.HasValue && bitrate.Value <= 320) { @@ -473,7 +466,7 @@ namespace MediaBrowser.Model.Dlna return MediaFormatProfile.AAC_ADTS; } - public MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height) + public static MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height) { if (string.Equals(container, "jpeg", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "jpg", StringComparison.OrdinalIgnoreCase)) @@ -499,7 +492,7 @@ namespace MediaBrowser.Model.Dlna return null; } - private MediaFormatProfile ResolveImageJPGFormat(int? width, int? height) + private static MediaFormatProfile ResolveImageJPGFormat(int? width, int? height) { if (width.HasValue && height.HasValue) { @@ -524,7 +517,7 @@ namespace MediaBrowser.Model.Dlna return MediaFormatProfile.JPEG_SM; } - private MediaFormatProfile ResolveImagePNGFormat(int? width, int? height) + private static MediaFormatProfile ResolveImagePNGFormat(int? width, int? height) { if (width.HasValue && height.HasValue) { From 820a3730161b98d5510847feee684797c23a3510 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 22:27:11 +0000 Subject: [PATCH 222/402] Bump Microsoft.Extensions.Diagnostics.HealthChecks from 5.0.3 to 5.0.4 Bumps [Microsoft.Extensions.Diagnostics.HealthChecks](https://github.com/dotnet/aspnetcore) from 5.0.3 to 5.0.4. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.3...v5.0.4) Signed-off-by: dependabot[bot] --- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index b7406d849..e7f83aff1 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,7 +37,7 @@ - + From 7c7020532666714dc22ac87356b059c6d4c5520c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 22:42:12 +0000 Subject: [PATCH 223/402] Bump Microsoft.NET.Test.Sdk from 16.9.1 to 16.9.4 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.9.1 to 16.9.4. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.9.1...v16.9.4) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 2 +- .../Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj | 2 +- tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 2 +- .../Jellyfin.MediaEncoding.Tests.csproj | 2 +- tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj | 2 +- tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 2 +- .../Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- .../Jellyfin.XbmcMetadata.Tests.csproj | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 577b61d02..57277be15 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -20,7 +20,7 @@ - + diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 017a67e9f..8018b2966 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index c56ccb365..ad1627698 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index 5d52f94c0..c2c0dca1b 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 4cc1d37ee..8321d0255 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -21,7 +21,7 @@ - + diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index 0c7e262f5..c5b51ef76 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index cc12a99a6..ebb134fc3 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index a76c0e9a0..d5268facc 100644 --- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index c3c258b68..fe26ec3ae 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -24,7 +24,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 34cef9005..079021e61 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index a310b0ea9..3ba49ff6a 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index 9380fe2af..4132205c3 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -16,7 +16,7 @@ - + From 0a00a73fec7822a1a10b3fc48494108651ab1a98 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 3 Apr 2021 01:46:31 +0200 Subject: [PATCH 224/402] Remove useless null check --- .../Json/Converters/JsonCommaDelimitedArrayConverter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs index d9f6519e9..2ec702165 100644 --- a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs @@ -26,8 +26,8 @@ namespace MediaBrowser.Common.Json.Converters { if (reader.TokenType == JsonTokenType.String) { - var stringEntries = reader.GetString()?.Split(',', StringSplitOptions.RemoveEmptyEntries); - if (stringEntries == null || stringEntries.Length == 0) + var stringEntries = reader.GetString().Split(',', StringSplitOptions.RemoveEmptyEntries); + if (stringEntries.Length == 0) { return Array.Empty(); } From 31d1dbfda630e45a8884f1a10d172f8cc83df9b0 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 3 Apr 2021 06:54:09 -0600 Subject: [PATCH 225/402] Add SessionDiscoveryInfo to generated api-docs --- .../Extensions/ApiServiceCollectionExtensions.cs | 2 +- .../{WebsocketModelFilter.cs => AdditionalModelFilter.cs} | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) rename Jellyfin.Server/Filters/{WebsocketModelFilter.cs => AdditionalModelFilter.cs} (82%) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index d26ac251c..233eab21b 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -319,7 +319,7 @@ namespace Jellyfin.Server.Extensions c.OperationFilter(); c.OperationFilter(); c.OperationFilter(); - c.DocumentFilter(); + c.DocumentFilter(); }); } diff --git a/Jellyfin.Server/Filters/WebsocketModelFilter.cs b/Jellyfin.Server/Filters/AdditionalModelFilter.cs similarity index 82% rename from Jellyfin.Server/Filters/WebsocketModelFilter.cs rename to Jellyfin.Server/Filters/AdditionalModelFilter.cs index 38afb201d..87a59e0b4 100644 --- a/Jellyfin.Server/Filters/WebsocketModelFilter.cs +++ b/Jellyfin.Server/Filters/AdditionalModelFilter.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Plugins; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.ApiClient; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; using MediaBrowser.Model.SyncPlay; @@ -9,9 +10,9 @@ using Swashbuckle.AspNetCore.SwaggerGen; namespace Jellyfin.Server.Filters { /// - /// Add models used in websocket messaging. + /// Add models not directly used by the API, but used for discovery and websockets. /// - public class WebsocketModelFilter : IDocumentFilter + public class AdditionalModelFilter : IDocumentFilter { /// public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) @@ -27,6 +28,7 @@ namespace Jellyfin.Server.Filters context.SchemaGenerator.GenerateSchema(typeof(GroupUpdate), context.SchemaRepository); context.SchemaGenerator.GenerateSchema(typeof(SessionMessageType), context.SchemaRepository); + context.SchemaGenerator.GenerateSchema(typeof(ServerDiscoveryInfo), context.SchemaRepository); } } } From 36da7a06d7f433d88ef94b770036815847e4dd89 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> Date: Sun, 4 Apr 2021 02:09:57 +0300 Subject: [PATCH 226/402] Less negation Co-authored-by: Bond-009 --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 167fc984e..b7327bc12 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -514,7 +514,7 @@ namespace MediaBrowser.Model.Dlna private static List GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable directPlayProfiles) { - var mediaType = videoStream != null ? DlnaProfileType.Video : DlnaProfileType.Audio; + var mediaType = videoStream == null ? DlnaProfileType.Audio : DlnaProfileType.Video; var containerSupported = false; var audioSupported = false; From 2ed0801be25921335b6509b6ebf9af61ad458409 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 4 Apr 2021 01:45:15 +0200 Subject: [PATCH 227/402] Fix possible nullref when `ProviderManager.SaveMetadata` gets called before `ProviderManager.AddParts` ``` Error Message: System.ArgumentNullException : Value cannot be null. (Parameter 'source') Stack Trace: at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at MediaBrowser.Providers.Manager.ProviderManager.SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable`1 savers) in D:\a\1\s\MediaBrowser.Providers\Manager\ProviderManager.cs:line 674 at MediaBrowser.Providers.Manager.ProviderManager.SaveMetadata(BaseItem item, ItemUpdateType updateType) in D:\a\1\s\MediaBrowser.Providers\Manager\ProviderManager.cs:line 655 at Emby.Server.Implementations.Library.LibraryManager.RunMetadataSavers(BaseItem item, ItemUpdateType updateReason) in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 2012 at Emby.Server.Implementations.Library.LibraryManager.UpdateItemsAsync(IReadOnlyList`1 items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 1970 at Emby.Server.Implementations.Library.LibraryManager.CreateRootFolder() in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 775 at Emby.Server.Implementations.Library.LibraryManager.get_RootFolder() in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 180 at Emby.Server.Implementations.IO.LibraryMonitor.Start() in D:\a\1\s\Emby.Server.Implementations\IO\LibraryMonitor.cs:line 135 at Emby.Server.Implementations.IO.LibraryMonitorStartup.RunAsync() in D:\a\1\s\Emby.Server.Implementations\IO\LibraryMonitorStartup.cs:line 26 at Emby.Server.Implementations.ApplicationHost.StartEntryPoints(IEnumerable`1 entryPoints, Boolean isBeforeStartup)+MoveNext() in D:\a\1\s\Emby.Server.Implementations\ApplicationHost.cs:line 541 at System.Threading.Tasks.Task.WhenAll(IEnumerable`1 tasks) at Emby.Server.Implementations.ApplicationHost.RunStartupTasksAsync(CancellationToken cancellationToken) in D:\a\1\s\Emby.Server.Implementations\ApplicationHost.cs:line 525 at Jellyfin.Server.Integration.Tests.JellyfinApplicationFactory.CreateServer(IWebHostBuilder builder) in D:\a\1\s\tests\Jellyfin.Server.Integration.Tests\JellyfinApplicationFactory.cs:line 101 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer() at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient() at Jellyfin.Server.Integration.Tests.OpenApiSpecTests.GetSpec_ReturnsCorrectResponse() in D:\a\1\s\tests\Jellyfin.Server.Integration.Tests\OpenApiSpecTests.cs:line 26 --- End of stack trace from previous location --- ``` --- MediaBrowser.Controller/Providers/IProviderManager.cs | 7 +++++-- MediaBrowser.Providers/Manager/ProviderManager.cs | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 2f5b1d4a3..7bc56c82a 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -91,8 +91,11 @@ namespace MediaBrowser.Controller.Providers /// /// Adds the metadata providers. /// - void AddParts(IEnumerable imageProviders, IEnumerable metadataServices, IEnumerable metadataProviders, - IEnumerable savers, + void AddParts( + IEnumerable imageProviders, + IEnumerable metadataServices, + IEnumerable metadataProviders, + IEnumerable metadataSavers, IEnumerable externalIds); /// diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index d581dd434..b4b0b826f 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -60,8 +60,8 @@ namespace MediaBrowser.Providers.Manager private IMetadataService[] _metadataServices = Array.Empty(); private IMetadataProvider[] _metadataProviders = Array.Empty(); - private IEnumerable _savers; - private IExternalId[] _externalIds; + private IMetadataSaver[] _savers = Array.Empty(); + private IExternalId[] _externalIds = Array.Empty(); private bool _isProcessingRefreshQueue; private bool _disposed; @@ -125,7 +125,7 @@ namespace MediaBrowser.Providers.Manager _externalIds = externalIds.OrderBy(i => i.ProviderName).ToArray(); _savers = metadataSavers - .Where(i => !(i is IConfigurableProvider configurable) || configurable.IsEnabled) + .Where(i => i is not IConfigurableProvider configurable || configurable.IsEnabled) .ToArray(); } From e0f513232b0b03135fa09fe39862c10982cb469e Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sun, 4 Apr 2021 14:58:20 +0200 Subject: [PATCH 228/402] Reduce nesting --- MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index b2d4db894..e65c16ee2 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -85,12 +85,14 @@ namespace MediaBrowser.MediaEncoding.Probing { var val = GetDictionaryValue(tags, key); - if (!string.IsNullOrEmpty(val)) + if (string.IsNullOrEmpty(val)) { - if (DateTime.TryParse(val, out var i)) - { - return i.ToUniversalTime(); - } + return null; + } + + if (DateTime.TryParse(val, out var i)) + { + return i.ToUniversalTime(); } return null; From 8d27e10cb696fb440ec7773aae69441d0651e64a Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sun, 4 Apr 2021 15:04:01 +0200 Subject: [PATCH 229/402] Interpret ffprobe date as UTC Currently, dates are parsed according to the local time, which results in potentially wrong data being stored in the database after normalizing to UTC - e.g. 2021-04-04 would be stored as '2021-04-03 22:00:00Z' and displayed in the UI as 03.04.2021. --- MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index e65c16ee2..da37687e8 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; namespace MediaBrowser.MediaEncoding.Probing { @@ -90,7 +91,7 @@ namespace MediaBrowser.MediaEncoding.Probing return null; } - if (DateTime.TryParse(val, out var i)) + if (DateTime.TryParse(val, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AssumeUniversal, out var i)) { return i.ToUniversalTime(); } From 873ad72c1893e87d1f8a0a744ebfe53b87781521 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sun, 4 Apr 2021 15:08:08 +0200 Subject: [PATCH 230/402] Support MKV DATE_RELEASED tag for PremiereDate https://www.matroska.org/technical/tagging.html#temporal-information --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 75067315f..a87104cd6 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -131,6 +131,7 @@ namespace MediaBrowser.MediaEncoding.Probing info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "date_released") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "date"); if (isAudio) From e6d487f7ea311c91c307138f4d38dbd25281f7cf Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 5 Apr 2021 01:53:00 +0200 Subject: [PATCH 231/402] Add test for ProbeResultNormalizer.GetMediaInfo --- .../Probing/ProbeResultNormalizerTests.cs | 56 ++++++++++++++ .../Test Data/Probing/some_matadata.json | 74 +++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs create mode 100644 tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/some_matadata.json diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs new file mode 100644 index 000000000..69e2aa437 --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -0,0 +1,56 @@ +using System.IO; +using System.Text.Json; +using MediaBrowser.Common.Json; +using MediaBrowser.MediaEncoding.Probing; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; +using Microsoft.Extensions.Logging.Abstractions; +using Xunit; + +namespace Jellyfin.MediaEncoding.Tests.Probing +{ + public class ProbeResultNormalizerTests + { + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private readonly ProbeResultNormalizer _probeResultNormalizer = new ProbeResultNormalizer(new NullLogger(), null); + + [Fact] + public void GetMediaInfo_MetaData_Success() + { + var bytes = File.ReadAllBytes("Test Data/Probing/some_matadata.json"); + var internalMediaInfoResult = JsonSerializer.Deserialize(bytes, _jsonOptions); + MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/some_matadata.mkv", MediaProtocol.File); + + Assert.Single(res.MediaStreams); + + Assert.NotNull(res.VideoStream); + Assert.Equal("4:3", res.VideoStream.AspectRatio); + Assert.Equal(25f, res.VideoStream.AverageFrameRate); + Assert.Equal(8, res.VideoStream.BitDepth); + Assert.Equal(69432, res.VideoStream.BitRate); + Assert.Equal("h264", res.VideoStream.Codec); + Assert.Equal("1/50", res.VideoStream.CodecTimeBase); + Assert.Equal(240, res.VideoStream.Height); + Assert.Equal(320, res.VideoStream.Width); + Assert.Equal(0, res.VideoStream.Index); + Assert.False(res.VideoStream.IsAnamorphic); + Assert.True(res.VideoStream.IsAVC); + Assert.True(res.VideoStream.IsDefault); + Assert.False(res.VideoStream.IsExternal); + Assert.False(res.VideoStream.IsForced); + Assert.False(res.VideoStream.IsInterlaced); + Assert.False(res.VideoStream.IsTextSubtitleStream); + Assert.Equal(13d, res.VideoStream.Level); + Assert.Equal("4", res.VideoStream.NalLengthSize); + Assert.Equal("yuv444p", res.VideoStream.PixelFormat); + Assert.Equal("High 4:4:4 Predictive", res.VideoStream.Profile); + Assert.Equal(25f, res.VideoStream.RealFrameRate); + Assert.Equal(1, res.VideoStream.RefFrames); + Assert.Equal("1/1000", res.VideoStream.TimeBase); + Assert.Equal(MediaStreamType.Video, res.VideoStream.Type); + + Assert.Empty(res.Chapters); + Assert.Equal("Just color bars", res.Overview); + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/some_matadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/some_matadata.json new file mode 100644 index 000000000..720fc5c8f --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/some_matadata.json @@ -0,0 +1,74 @@ +{ + "streams": [ + { + "index": 0, + "codec_name": "h264", + "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", + "profile": "High 4:4:4 Predictive", + "codec_type": "video", + "codec_time_base": "1/50", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "width": 320, + "height": 240, + "coded_width": 320, + "coded_height": 240, + "closed_captions": 0, + "has_b_frames": 2, + "sample_aspect_ratio": "1:1", + "display_aspect_ratio": "4:3", + "pix_fmt": "yuv444p", + "level": 13, + "chroma_location": "left", + "field_order": "progressive", + "refs": 1, + "is_avc": "true", + "nal_length_size": "4", + "r_frame_rate": "25/1", + "avg_frame_rate": "25/1", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "bits_per_raw_sample": "8", + "disposition": { + "default": 1, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + }, + "tags": { + "ENCODER": "Lavc57.107.100 libx264", + "DURATION": "00:00:01.000000000" + } + } + ], + "chapters": [ + + ], + "format": { + "filename": "some_metadata.mkv", + "nb_streams": 1, + "nb_programs": 0, + "format_name": "matroska,webm", + "format_long_name": "Matroska / WebM", + "start_time": "0.000000", + "duration": "1.000000", + "size": "8679", + "bit_rate": "69432", + "probe_score": 100, + "tags": { + "DESCRIPTION": "Just color bars", + "ARCHIVAL": "yes", + "PRESERVE_THIS": "okay", + "ENCODER": "Lavf57.83.100" + } + } +} From 14d0acf28572c6b5f5ecac6728b7a8184683b5f2 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 5 Apr 2021 15:12:47 +0200 Subject: [PATCH 232/402] add simple auth handling to websocketmanager --- Emby.Server.Implementations/HttpServer/WebSocketManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs index d6cf6233e..1bee1ac31 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs @@ -14,15 +14,18 @@ namespace Emby.Server.Implementations.HttpServer public class WebSocketManager : IWebSocketManager { private readonly IWebSocketListener[] _webSocketListeners; + private readonly IAuthService _authService; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; public WebSocketManager( + IAuthService authService, IEnumerable webSocketListeners, ILogger logger, ILoggerFactory loggerFactory) { _webSocketListeners = webSocketListeners.ToArray(); + _authService = authService; _logger = logger; _loggerFactory = loggerFactory; } @@ -30,6 +33,7 @@ namespace Emby.Server.Implementations.HttpServer /// public async Task WebSocketRequestHandler(HttpContext context) { + _ = _authService.Authenticate(context.Request); try { _logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress); From ba958c70d5359183660f94542954585bb6f67d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Tue, 6 Apr 2021 17:24:01 +0200 Subject: [PATCH 233/402] (jellyfin-web): Switch to npm --- Dockerfile | 2 +- Dockerfile.arm | 2 +- Dockerfile.arm64 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 41dd3d081..6552546ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ - && yarn install \ + && npm i \ && mv dist /dist FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim as builder diff --git a/Dockerfile.arm b/Dockerfile.arm index e0eaca0ed..1829982f5 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -10,7 +10,7 @@ ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ - && yarn install \ + && npm i \ && mv dist /dist diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index db7de935c..994df32dd 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -10,7 +10,7 @@ ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ - && yarn install \ + && npm i \ && mv dist /dist From 8d6713af65e730c4508cea241439db315217ee8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Tue, 6 Apr 2021 17:33:51 +0200 Subject: [PATCH 234/402] Use npm ci instead of npm i --- Dockerfile | 2 +- Dockerfile.arm | 2 +- Dockerfile.arm64 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6552546ea..cafee737a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ - && npm i \ + && npm ci --no-audit \ && mv dist /dist FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim as builder diff --git a/Dockerfile.arm b/Dockerfile.arm index 1829982f5..d63dbee75 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -10,7 +10,7 @@ ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ - && npm i \ + && npm ci --no-audit \ && mv dist /dist diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 994df32dd..e95999f2a 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -10,7 +10,7 @@ ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ - && npm i \ + && npm ci --no-audit \ && mv dist /dist From 95327b842e9eb50ca2c53740674b8ed2f6615eae Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 6 Apr 2021 20:02:06 +0200 Subject: [PATCH 235/402] Enable NetAnalyzers for more projects --- Emby.Drawing/NullImageEncoder.cs | 2 +- Emby.Notifications/Emby.Notifications.csproj | 6 ++---- Emby.Photos/Emby.Photos.csproj | 6 ++---- .../Emby.Server.Implementations.csproj | 6 ++---- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs index 2a1cfd3da..1c05aa916 100644 --- a/Emby.Drawing/NullImageEncoder.cs +++ b/Emby.Drawing/NullImageEncoder.cs @@ -32,7 +32,7 @@ namespace Emby.Drawing => throw new NotImplementedException(); /// - public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) + public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat) { throw new NotImplementedException(); } diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 526a27229..5a2aea642 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -11,6 +11,8 @@ true true enable + AllEnabledByDefault + ../jellyfin.ruleset @@ -30,8 +32,4 @@ - - ../jellyfin.ruleset - - diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index e64a658c5..2b6618159 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -24,6 +24,8 @@ true true enable + AllEnabledByDefault + ../jellyfin.ruleset @@ -33,8 +35,4 @@ - - ../jellyfin.ruleset - - diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index be552ef93..9248053f5 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -46,6 +46,8 @@ true AD0001 + AllEnabledByDefault + ../jellyfin.ruleset @@ -55,10 +57,6 @@ - - ../jellyfin.ruleset - - From e85ac4d975c19e47637e11e76294de90e0481309 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Apr 2021 18:35:36 +0000 Subject: [PATCH 236/402] Bump Microsoft.AspNetCore.Authorization from 5.0.3 to 5.0.5 Bumps [Microsoft.AspNetCore.Authorization](https://github.com/dotnet/aspnetcore) from 5.0.3 to 5.0.5. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.3...v5.0.5) Signed-off-by: dependabot[bot] --- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index d5372d752..d6dc5a2dc 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -15,7 +15,7 @@ - + From 3651ee5ed32273b1b6e7f63afe0b9923235e4d14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Apr 2021 18:35:51 +0000 Subject: [PATCH 237/402] Bump Microsoft.AspNetCore.Mvc.Testing from 5.0.3 to 5.0.5 Bumps [Microsoft.AspNetCore.Mvc.Testing](https://github.com/dotnet/aspnetcore) from 5.0.3 to 5.0.5. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.3...v5.0.5) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 57277be15..f288561b7 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 079021e61..8d4d9e3d2 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 3ba49ff6a..4a5cf1fee 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -13,7 +13,7 @@ - + From 65f880be323e154655505c702822ce381fc5569e Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 6 Apr 2021 20:59:47 +0100 Subject: [PATCH 238/402] Keep plugin status after update. --- Emby.Server.Implementations/Plugins/PluginManager.cs | 7 ++++--- .../Updates/InstallationManager.cs | 12 +++++++----- MediaBrowser.Common/Plugins/IPluginManager.cs | 4 +++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 700396c4c..3a8296455 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; @@ -368,7 +369,7 @@ namespace Emby.Server.Implementations.Plugins } /// - public async Task GenerateManifest(PackageInfo packageInfo, Version version, string path) + public async Task GenerateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status) { if (packageInfo == null) { @@ -411,9 +412,9 @@ namespace Emby.Server.Implementations.Plugins Overview = packageInfo.Overview, Owner = packageInfo.Owner, TargetAbi = versionInfo.TargetAbi ?? string.Empty, - Timestamp = string.IsNullOrEmpty(versionInfo.Timestamp) ? DateTime.MinValue : DateTime.Parse(versionInfo.Timestamp), + Timestamp = string.IsNullOrEmpty(versionInfo.Timestamp) ? DateTime.MinValue : DateTime.Parse(versionInfo.Timestamp, CultureInfo.InvariantCulture), Version = versionInfo.Version, - Status = PluginStatus.Active, + Status = status == PluginStatus.Disabled ? PluginStatus.Disabled : PluginStatus.Active, // Keep disabled state. AutoUpdate = true, ImagePath = imagePath }; diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index fc34f93cd..e7307aefe 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -22,6 +22,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Updates; using Microsoft.Extensions.Logging; @@ -194,7 +195,7 @@ namespace Emby.Server.Implementations.Updates var plugin = _pluginManager.GetPlugin(packageGuid, version.VersionNumber); if (plugin != null) { - await _pluginManager.GenerateManifest(package, version.VersionNumber, plugin.Path); + await _pluginManager.GenerateManifest(package, version.VersionNumber, plugin.Path, plugin.Manifest.Status).ConfigureAwait(false); } // Remove versions with a target ABI greater then the current application version. @@ -500,7 +501,8 @@ namespace Emby.Server.Implementations.Updates var plugins = _pluginManager.Plugins; foreach (var plugin in plugins) { - if (plugin.Manifest?.AutoUpdate == false) + // Don't auto update when plugin marked not to, or when it's disabled. + if (plugin.Manifest?.AutoUpdate == false || plugin.Manifest?.Status == MediaBrowser.Model.Plugins.PluginStatus.Disabled) { continue; } @@ -515,7 +517,7 @@ namespace Emby.Server.Implementations.Updates } } - private async Task PerformPackageInstallation(InstallationInfo package, CancellationToken cancellationToken) + private async Task PerformPackageInstallation(InstallationInfo package, PluginStatus status, CancellationToken cancellationToken) { var extension = Path.GetExtension(package.SourceUrl); if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase)) @@ -567,7 +569,7 @@ namespace Emby.Server.Implementations.Updates stream.Position = 0; _zipClient.ExtractAllFromZip(stream, targetDir, true); - await _pluginManager.GenerateManifest(package.PackageInfo, package.Version, targetDir); + await _pluginManager.GenerateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false); _pluginManager.ImportPluginFrom(targetDir); } @@ -576,7 +578,7 @@ namespace Emby.Server.Implementations.Updates LocalPlugin? plugin = _pluginManager.Plugins.FirstOrDefault(p => p.Id.Equals(package.Id) && p.Version.Equals(package.Version)) ?? _pluginManager.Plugins.FirstOrDefault(p => p.Name.Equals(package.Name, StringComparison.OrdinalIgnoreCase) && p.Version.Equals(package.Version)); - await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false); + await PerformPackageInstallation(package, plugin?.Manifest.Status ?? PluginStatus.Active, cancellationToken).ConfigureAwait(false); _logger.LogInformation(plugin == null ? "New plugin installed: {PluginName} {PluginVersion}" : "Plugin updated: {PluginName} {PluginVersion}", package.Name, package.Version); return plugin != null; diff --git a/MediaBrowser.Common/Plugins/IPluginManager.cs b/MediaBrowser.Common/Plugins/IPluginManager.cs index f9a8fb6f7..0e2e814cb 100644 --- a/MediaBrowser.Common/Plugins/IPluginManager.cs +++ b/MediaBrowser.Common/Plugins/IPluginManager.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; +using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Updates; using Microsoft.Extensions.DependencyInjection; @@ -51,8 +52,9 @@ namespace MediaBrowser.Common.Plugins /// The used to generate a manifest. /// Version to be installed. /// The path where to save the manifest. + /// Initial status of the plugin. /// True if successful. - Task GenerateManifest(PackageInfo packageInfo, Version version, string path); + Task GenerateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status); /// /// Imports plugin details from a folder. From fbd9141ecdb6ecd8c8b887c33b0b04370f7002e3 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 6 Apr 2021 22:46:54 +0200 Subject: [PATCH 239/402] Add tests for unauthenticated websocket access --- .../WebSocketTests.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/Jellyfin.Server.Integration.Tests/WebSocketTests.cs diff --git a/tests/Jellyfin.Server.Integration.Tests/WebSocketTests.cs b/tests/Jellyfin.Server.Integration.Tests/WebSocketTests.cs new file mode 100644 index 000000000..ffdc04eba --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/WebSocketTests.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests +{ + public sealed class WebSocketTests : IClassFixture + { + private readonly JellyfinApplicationFactory _factory; + + public WebSocketTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task WebSocket_Unauthenticated_ThrowsInvalidOperationException() + { + var server = _factory.Server; + var client = server.CreateWebSocketClient(); + + await Assert.ThrowsAsync( + () => client.ConnectAsync( + new UriBuilder(server.BaseAddress) + { + Scheme = "ws", + Path = "websocket" + }.Uri, CancellationToken.None)); + } + } +} From d772fddfb3fcddd5f540cf2650eb7e405a6aec2c Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 7 Apr 2021 13:09:00 +0200 Subject: [PATCH 240/402] make custompref value nullable --- .../Entities/CustomItemDisplayPreferences.cs | 4 +- ...110544_NullableCustomPrefValue.Designer.cs | 520 ++++++++++++++++++ .../20210407110544_NullableCustomPrefValue.cs | 35 ++ .../Migrations/JellyfinDbModelSnapshot.cs | 10 +- .../Users/DisplayPreferencesManager.cs | 2 +- .../IDisplayPreferencesManager.cs | 2 +- 6 files changed, 563 insertions(+), 10 deletions(-) create mode 100644 Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs diff --git a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs index cc46248c7..de37fb544 100644 --- a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs @@ -17,7 +17,7 @@ namespace Jellyfin.Data.Entities /// The client. /// The preference key. /// The preference value. - public CustomItemDisplayPreferences(Guid userId, Guid itemId, string client, string key, string value) + public CustomItemDisplayPreferences(Guid userId, Guid itemId, string client, string key, string? value) { UserId = userId; ItemId = itemId; @@ -75,6 +75,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - public string Value { get; set; } + public string? Value { get; set; } } } diff --git a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs new file mode 100644 index 000000000..d332d19f2 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs @@ -0,0 +1,520 @@ +#pragma warning disable CS1591 +// +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + [Migration("20210407110544_NullableCustomPrefValue")] + partial class NullableCustomPrefValue + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "5.0.3"); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("EndHour") + .HasColumnType("REAL"); + + b.Property("StartHour") + .HasColumnType("REAL"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccessSchedules"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateCreated") + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("ShortOverview") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "ItemId", "Client", "Key") + .IsUnique(); + + b.ToTable("CustomItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChromecastVersion") + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("DashboardTheme") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("EnableNextVideoInfoOverlay") + .HasColumnType("INTEGER"); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("ScrollDirection") + .HasColumnType("INTEGER"); + + b.Property("ShowBackdrop") + .HasColumnType("INTEGER"); + + b.Property("ShowSidebar") + .HasColumnType("INTEGER"); + + b.Property("SkipBackwardLength") + .HasColumnType("INTEGER"); + + b.Property("SkipForwardLength") + .HasColumnType("INTEGER"); + + b.Property("TvHome") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "ItemId", "Client") + .IsUnique(); + + b.ToTable("DisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DisplayPreferencesId") + .HasColumnType("INTEGER"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DisplayPreferencesId"); + + b.ToTable("HomeSection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("ImageInfos"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Client") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("IndexBy") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("TEXT"); + + b.Property("RememberIndexing") + .HasColumnType("INTEGER"); + + b.Property("RememberSorting") + .HasColumnType("INTEGER"); + + b.Property("SortBy") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("ViewType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ItemDisplayPreferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Permission_Permissions_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Permission_Permissions_Guid"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Preference_Preferences_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Preference_Preferences_Guid"); + + b.ToTable("Preferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AudioLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("AuthenticationProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property("EasyPassword") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property("EnableAutoLogin") + .HasColumnType("INTEGER"); + + b.Property("EnableLocalPassword") + .HasColumnType("INTEGER"); + + b.Property("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property("InternalId") + .HasColumnType("INTEGER"); + + b.Property("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property("LastLoginDate") + .HasColumnType("TEXT"); + + b.Property("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property("MaxActiveSessions") + .HasColumnType("INTEGER"); + + b.Property("MaxParentalAgeRating") + .HasColumnType("INTEGER"); + + b.Property("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasMaxLength(65535) + .HasColumnType("TEXT"); + + b.Property("PasswordResetProviderId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property("RemoteClientBitrateLimit") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SubtitleLanguagePreference") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("SubtitleMode") + .HasColumnType("INTEGER"); + + b.Property("SyncPlayAccess") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("AccessSchedules") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("DisplayPreferences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b => + { + b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null) + .WithMany("HomeSections") + .HasForeignKey("DisplayPreferencesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("ProfileImage") + .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("ItemDisplayPreferences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Permissions") + .HasForeignKey("Permission_Permissions_Guid"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Guid"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => + { + b.Navigation("HomeSections"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Navigation("AccessSchedules"); + + b.Navigation("DisplayPreferences"); + + b.Navigation("ItemDisplayPreferences"); + + b.Navigation("Permissions"); + + b.Navigation("Preferences"); + + b.Navigation("ProfileImage"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs new file mode 100644 index 000000000..ade68612c --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.cs @@ -0,0 +1,35 @@ +#pragma warning disable CS1591 +// +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class NullableCustomPrefValue : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Value", + schema: "jellyfin", + table: "CustomItemDisplayPreferences", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Value", + schema: "jellyfin", + table: "CustomItemDisplayPreferences", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 1614a88ef..6a523ba68 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("jellyfin") - .HasAnnotation("ProductVersion", "5.0.0"); + .HasAnnotation("ProductVersion", "5.0.3"); modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => { @@ -110,7 +110,6 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("TEXT"); b.Property("Value") - .IsRequired() .HasColumnType("TEXT"); b.HasKey("Id"); @@ -448,8 +447,8 @@ namespace Jellyfin.Server.Implementations.Migrations modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b => { b.HasOne("Jellyfin.Data.Entities.User", null) - .WithOne("DisplayPreferences") - .HasForeignKey("Jellyfin.Data.Entities.DisplayPreferences", "UserId") + .WithMany("DisplayPreferences") + .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); @@ -502,8 +501,7 @@ namespace Jellyfin.Server.Implementations.Migrations { b.Navigation("AccessSchedules"); - b.Navigation("DisplayPreferences") - .IsRequired(); + b.Navigation("DisplayPreferences"); b.Navigation("ItemDisplayPreferences"); diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index a3e9516b9..c89e3c74d 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -68,7 +68,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public IDictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client) + public Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client) { return _dbContext.CustomItemDisplayPreferences .AsQueryable() diff --git a/MediaBrowser.Controller/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/IDisplayPreferencesManager.cs index 041eeea62..87b6f81c5 100644 --- a/MediaBrowser.Controller/IDisplayPreferencesManager.cs +++ b/MediaBrowser.Controller/IDisplayPreferencesManager.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Controller /// The item id. /// The client string. /// The dictionary of custom item display preferences. - IDictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); + Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); /// /// Sets the custom item display preference for the user and client. From 4892e0cf064a2a392881b38a5965324aa80eac0c Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 7 Apr 2021 13:17:59 +0200 Subject: [PATCH 241/402] fix build...somehow --- MediaBrowser.Controller/IDisplayPreferencesManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/IDisplayPreferencesManager.cs index 87b6f81c5..be1d974a4 100644 --- a/MediaBrowser.Controller/IDisplayPreferencesManager.cs +++ b/MediaBrowser.Controller/IDisplayPreferencesManager.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Controller /// The item id. /// The client string. /// The dictionary of custom item display preferences. - Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); + Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); /// /// Sets the custom item display preference for the user and client. From 45a16c19a76d8fac36d3acf864f8030fb30d4e1c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 7 Apr 2021 21:29:15 +0200 Subject: [PATCH 242/402] Add code to test authenticated endpoints --- .../AuthHelper.cs | 59 +++++++++++++++++++ .../Controllers/ActivityLogControllerTests.cs | 30 ++++++++++ 2 files changed, 89 insertions(+) create mode 100644 tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs create mode 100644 tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs diff --git a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs new file mode 100644 index 000000000..4b8fac07f --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs @@ -0,0 +1,59 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Text.Json; +using System.Threading.Tasks; +using Jellyfin.Api.Models.StartupDtos; +using Jellyfin.Api.Models.UserDtos; +using MediaBrowser.Common.Json; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests +{ + public static class AuthHelper + { + public const string AuthHeaderName = "X-Emby-Authorization"; + public const string DummyAuthHeader = "MediaBrowser Client=\"Jellyfin.Server Integration Tests\", DeviceId=\"69420\", Device=\"Apple II\", Version=\"10.8.0\""; + + public static async Task CompleteStartupAsync(HttpClient client) + { + var jsonOptions = JsonDefaults.Options; + var userResponse = await client.GetByteArrayAsync("/Startup/User").ConfigureAwait(false); + var user = JsonSerializer.Deserialize(userResponse, jsonOptions); + + using var completeResponse = await client.PostAsync("/Startup/Complete", new ByteArrayContent(Array.Empty())).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, completeResponse.StatusCode); + + using var content = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes( + new AuthenticateUserByName() + { + Username = user!.Name, + Pw = user.Password, + }, + jsonOptions)); + content.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + content.Headers.Add("X-Emby-Authorization", DummyAuthHeader); + + using var authResponse = await client.PostAsync("/Users/AuthenticateByName", content).ConfigureAwait(false); + var auth = await JsonSerializer.DeserializeAsync( + await authResponse.Content.ReadAsStreamAsync().ConfigureAwait(false), + jsonOptions).ConfigureAwait(false); + + return auth!.AccessToken; + } + + public static void AddAuthHeader(this HttpRequestHeaders headers, string accessToken) + { + headers.Add(AuthHeaderName, DummyAuthHeader + $", Token={accessToken}"); + } + + private class AuthenticationResultDto + { + public string AccessToken { get; set; } = string.Empty; + + public string ServerId { get; set; } = string.Empty; + } + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs new file mode 100644 index 000000000..4657ee6e2 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs @@ -0,0 +1,30 @@ +using System.Net; +using System.Net.Mime; +using System.Threading.Tasks; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests.Controllers +{ + public sealed class ActivityLogControllerTests : IClassFixture + { + private readonly JellyfinApplicationFactory _factory; + private string? _accessToken; + + public ActivityLogControllerTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task ActivityLog_GetEntries_Ok() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var response = await client.GetAsync("System/ActivityLog/Entries").ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType); + } + } +} From 7c457da9ab0803a467e8508203b527378066c099 Mon Sep 17 00:00:00 2001 From: Brian Arnold Date: Thu, 8 Apr 2021 02:39:58 -0400 Subject: [PATCH 243/402] Fixed issue with determining if a directory was a directory or file when it contained a '.' character in the directory path. Resolves: #2845 --- MediaBrowser.Controller/Playlists/Playlist.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index e8b7be7e2..faaceb828 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Text.Json.Serialization; using System.Threading; @@ -43,6 +44,16 @@ namespace MediaBrowser.Controller.Playlists public static bool IsPlaylistFile(string path) { + //When a directory contains a `.`, calling "HasExtension" will return true, even if that location is a directory that exists. + //This kills the PlaylistXmlSaver, because instead of saving in "config/data/playlists/MyList2.0/playlist.xml", + //It saves the information in "config/data/playlists/MyList2.xml". So we just need to see if it's actually a real directory first. + //Lucky for us, when a new playlist is created, the directory on the drive is created first, then the Playlist object is created. + //And if there's not a directory there, then we can just check if it has an extension. + if (new System.IO.DirectoryInfo(path).Exists) + { //This is a directory, and therefore definitely not a playlist file. + return false; + } + //Well, there's no directory there, so if it /looks/ like a file path, then it probably is. return System.IO.Path.HasExtension(path); } From 2314487e38d77f5d3a123ad2f0e76ab6ea2134c7 Mon Sep 17 00:00:00 2001 From: BrianCArnold Date: Thu, 8 Apr 2021 04:03:43 -0400 Subject: [PATCH 244/402] Update MediaBrowser.Controller/Playlists/Playlist.cs Included suggested change from cvium Co-authored-by: Claus Vium --- MediaBrowser.Controller/Playlists/Playlist.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index faaceb828..089d241ba 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -44,17 +44,8 @@ namespace MediaBrowser.Controller.Playlists public static bool IsPlaylistFile(string path) { - //When a directory contains a `.`, calling "HasExtension" will return true, even if that location is a directory that exists. - //This kills the PlaylistXmlSaver, because instead of saving in "config/data/playlists/MyList2.0/playlist.xml", - //It saves the information in "config/data/playlists/MyList2.xml". So we just need to see if it's actually a real directory first. - //Lucky for us, when a new playlist is created, the directory on the drive is created first, then the Playlist object is created. - //And if there's not a directory there, then we can just check if it has an extension. - if (new System.IO.DirectoryInfo(path).Exists) - { //This is a directory, and therefore definitely not a playlist file. - return false; - } - //Well, there's no directory there, so if it /looks/ like a file path, then it probably is. - return System.IO.Path.HasExtension(path); + // The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot). + return Path.HasExtension(path) && !Directory.Exists(path); } [JsonIgnore] From a2acfb02e994f9829c5a0131f583e720b3466ce8 Mon Sep 17 00:00:00 2001 From: Brian Arnold Date: Thu, 8 Apr 2021 05:19:28 -0400 Subject: [PATCH 245/402] Can't reference System.IO.Path as 'Path', even though System.IO is in the usings, because there is a Path property of the class. --- MediaBrowser.Controller/Playlists/Playlist.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 089d241ba..977b14cb0 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.Controller.Playlists public static bool IsPlaylistFile(string path) { // The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot). - return Path.HasExtension(path) && !Directory.Exists(path); + return System.IO.Path.HasExtension(path) && !Directory.Exists(path); } [JsonIgnore] From b1faf8c2e832d269c1bc6017037a17533913abf4 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 8 Apr 2021 07:36:13 -0600 Subject: [PATCH 246/402] Update to dotnet 5.0.5 --- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.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.macos | 2 +- deployment/Dockerfile.portable | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- deployment/Dockerfile.windows.amd64 | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 20 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index d5372d752..d6dc5a2dc 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -15,7 +15,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 96a4fa2fb..2c6a176b6 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -27,13 +27,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 e7f83aff1..61171c018 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index f622a042a..4db99f0b0 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -36,7 +36,7 @@ - + diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index 428072613..ec0321f47 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.debian.arm64 b/deployment/Dockerfile.debian.arm64 index b540efc09..8fd5ddb93 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.debian.armhf b/deployment/Dockerfile.debian.armhf index 426ce02fc..14615d19f 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.linux.amd64 b/deployment/Dockerfile.linux.amd64 index 3b91515f3..1f6ca1558 100644 --- a/deployment/Dockerfile.linux.amd64 +++ b/deployment/Dockerfile.linux.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl index 2ca9072ba..6af5d8baf 100644 --- a/deployment/Dockerfile.linux.amd64-musl +++ b/deployment/Dockerfile.linux.amd64-musl @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.linux.arm64 b/deployment/Dockerfile.linux.arm64 index 03efd306d..15b59e29d 100644 --- a/deployment/Dockerfile.linux.arm64 +++ b/deployment/Dockerfile.linux.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.linux.armhf b/deployment/Dockerfile.linux.armhf index 585572204..71a0fda21 100644 --- a/deployment/Dockerfile.linux.armhf +++ b/deployment/Dockerfile.linux.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.macos b/deployment/Dockerfile.macos index b37afdcfb..9291bcbb9 100644 --- a/deployment/Dockerfile.macos +++ b/deployment/Dockerfile.macos @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.portable b/deployment/Dockerfile.portable index 686b20197..e98ba74f8 100644 --- a/deployment/Dockerfile.portable +++ b/deployment/Dockerfile.portable @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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 3513bf8ec..d1fd8818e 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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 5acdf0d17..8e79d417c 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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 42f757d05..627caa95a 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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.windows.amd64 b/deployment/Dockerfile.windows.amd64 index 6ed1193fb..5723abcae 100644 --- a/deployment/Dockerfile.windows.amd64 +++ b/deployment/Dockerfile.windows.amd64 @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/a2052604-de46-4cd4-8256-9bc222537d32/a798771950904eaf91c0c37c58f516e1/dotnet-sdk-5.0.103-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-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/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 57277be15..f288561b7 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 079021e61..8d4d9e3d2 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 3ba49ff6a..4a5cf1fee 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -13,7 +13,7 @@ - + From b645bb20deb92225e17dd87fcc7fbf2fc1631e80 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 8 Apr 2021 14:38:25 +0100 Subject: [PATCH 247/402] Update Emby.Server.Implementations/Updates/InstallationManager.cs Co-authored-by: Cody Robibero --- Emby.Server.Implementations/Updates/InstallationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index e7307aefe..653b1381b 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -502,7 +502,7 @@ namespace Emby.Server.Implementations.Updates foreach (var plugin in plugins) { // Don't auto update when plugin marked not to, or when it's disabled. - if (plugin.Manifest?.AutoUpdate == false || plugin.Manifest?.Status == MediaBrowser.Model.Plugins.PluginStatus.Disabled) + if (plugin.Manifest?.AutoUpdate == false || plugin.Manifest?.Status == PluginStatus.Disabled) { continue; } From cc0f191228d7444a598370ac93ba7312700d58b1 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Thu, 8 Apr 2021 09:57:17 -0400 Subject: [PATCH 248/402] Disable hevc encoding by default --- MediaBrowser.Model/Configuration/EncodingOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index a9b280301..365bbeef6 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Model.Configuration EnableDecodingColorDepth10Vp9 = true; EnableEnhancedNvdecDecoder = true; EnableHardwareEncoding = true; - AllowHevcEncoding = true; + AllowHevcEncoding = false; EnableSubtitleExtraction = true; HardwareDecodingCodecs = new string[] { "h264", "vc1" }; } From 391554d391c1a045a0922fd5f0b43459afdfc550 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 9 Apr 2021 01:04:49 +0200 Subject: [PATCH 249/402] Add tests for UserController --- .../AuthHelper.cs | 2 +- .../Controllers/ActivityLogControllerTests.cs | 2 +- .../Controllers/UserControllerTests.cs | 170 ++++++++++++++++++ 3 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs diff --git a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs index 4b8fac07f..ea6838682 100644 --- a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs +++ b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs @@ -44,7 +44,7 @@ namespace Jellyfin.Server.Integration.Tests return auth!.AccessToken; } - public static void AddAuthHeader(this HttpRequestHeaders headers, string accessToken) + public static void AddAuthHeader(this HttpHeaders headers, string accessToken) { headers.Add(AuthHeaderName, DummyAuthHeader + $", Token={accessToken}"); } diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs index 4657ee6e2..be89fbc9a 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs @@ -8,7 +8,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers public sealed class ActivityLogControllerTests : IClassFixture { private readonly JellyfinApplicationFactory _factory; - private string? _accessToken; + private static string? _accessToken; public ActivityLogControllerTests(JellyfinApplicationFactory factory) { diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs new file mode 100644 index 000000000..6584490de --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs @@ -0,0 +1,170 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Text.Json; +using System.Threading.Tasks; +using Jellyfin.Api.Models.UserDtos; +using MediaBrowser.Common.Json; +using MediaBrowser.Model.Dto; +using Xunit; +using Xunit.Priority; + +namespace Jellyfin.Server.Integration.Tests.Controllers +{ + [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] + public sealed class UserControllerTests : IClassFixture + { + private const string TestUsername = "testUser01"; + + private readonly JellyfinApplicationFactory _factory; + private readonly JsonSerializerOptions _jsonOpions = JsonDefaults.Options; + private static string? _accessToken; + private static Guid _testUserId = Guid.Empty; + + public UserControllerTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + private Task CreateUserByName(HttpClient httpClient, CreateUserByName request) + { + using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(request, _jsonOpions)); + postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + return httpClient.PostAsync("Users/New", postContent); + } + + private Task UpdateUserPassword(HttpClient httpClient, Guid userId, UpdateUserPassword request) + { + using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(request, _jsonOpions)); + postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + return httpClient.PostAsync("Users/" + userId.ToString("N", CultureInfo.InvariantCulture) + "/Password", postContent); + } + + [Fact] + [Priority(-1)] + public async Task GetPublicUsers_Valid_Success() + { + var client = _factory.CreateClient(); + + using var response = await client.GetAsync("Users/Public").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var users = await JsonSerializer.DeserializeAsync( + await response.Content.ReadAsStreamAsync().ConfigureAwait(false), _jsonOpions).ConfigureAwait(false); + // User are hidden by default + Assert.Empty(users); + } + + [Fact] + [Priority(-1)] + public async Task GetUsers_Valid_Success() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + using var response = await client.GetAsync("Users").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var users = await JsonSerializer.DeserializeAsync( + await response.Content.ReadAsStreamAsync().ConfigureAwait(false), _jsonOpions).ConfigureAwait(false); + Assert.Single(users); + Assert.False(users![0].HasConfiguredPassword); + } + + [Fact] + [Priority(0)] + public async Task New_Valid_Success() + { + var client = _factory.CreateClient(); + + // access token can't be null here as the previous test populated it + client.DefaultRequestHeaders.AddAuthHeader(_accessToken!); + + var createRequest = new CreateUserByName() + { + Name = TestUsername + }; + + using var response = await CreateUserByName(client, createRequest).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var user = await JsonSerializer.DeserializeAsync( + await response.Content.ReadAsStreamAsync().ConfigureAwait(false), _jsonOpions).ConfigureAwait(false); + Assert.Equal(TestUsername, user!.Name); + Assert.False(user.HasPassword); + Assert.False(user.HasConfiguredPassword); + + _testUserId = user.Id; + + Console.WriteLine(user.Id.ToString("N", CultureInfo.InvariantCulture)); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData("‼️")] + [Priority(0)] + public async Task New_Invalid_Fail(string? username) + { + var client = _factory.CreateClient(); + + // access token can't be null here as the previous test populated it + client.DefaultRequestHeaders.AddAuthHeader(_accessToken!); + + var createRequest = new CreateUserByName() + { + Name = username + }; + + using var response = await CreateUserByName(client, createRequest).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + [Priority(1)] + public async Task UpdateUserPassword_Valid_Success() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken!); + + var createRequest = new UpdateUserPassword() + { + NewPw = "4randomPa$$word" + }; + + using var response = await UpdateUserPassword(client, _testUserId, createRequest).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + + var users = await JsonSerializer.DeserializeAsync( + await client.GetStreamAsync("Users").ConfigureAwait(false), _jsonOpions).ConfigureAwait(false); + var user = users!.First(x => x.Id == _testUserId); + Assert.True(user.HasPassword); + Assert.True(user.HasConfiguredPassword); + } + + [Fact] + [Priority(2)] + public async Task UpdateUserPassword_Empty_RemoveSetPassword() + { + var client = _factory.CreateClient(); + + client.DefaultRequestHeaders.AddAuthHeader(_accessToken!); + + var createRequest = new UpdateUserPassword() + { + CurrentPw = "4randomPa$$word", + }; + + using var response = await UpdateUserPassword(client, _testUserId, createRequest).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + + var users = await JsonSerializer.DeserializeAsync( + await client.GetStreamAsync("Users").ConfigureAwait(false), _jsonOpions).ConfigureAwait(false); + var user = users!.First(x => x.Id == _testUserId); + Assert.False(user.HasPassword); + Assert.False(user.HasConfiguredPassword); + } + } +} From db530e61f5ac7047f8c5b88b3a30aa17e9253bbc Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 9 Apr 2021 11:32:19 +0200 Subject: [PATCH 250/402] move IsPlayed to outerquery IsPlayed is a column in UserDatas and does not belong in the inner query. None of the other UserDatas columns are in the innerquery. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2ae805447..694805ebe 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5415,7 +5415,6 @@ AND Type = @InternalPersonType)"); ItemIds = query.ItemIds, TopParentIds = query.TopParentIds, ParentId = query.ParentId, - IsPlayed = query.IsPlayed, IsAiring = query.IsAiring, IsMovie = query.IsMovie, IsSports = query.IsSports, @@ -5441,6 +5440,7 @@ AND Type = @InternalPersonType)"); var outerQuery = new InternalItemsQuery(query.User) { + IsPlayed = query.IsPlayed, IsFavorite = query.IsFavorite, IsFavoriteOrLiked = query.IsFavoriteOrLiked, IsLiked = query.IsLiked, From a21b2714e7957640afa140361d9886877e57d6df Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 9 Apr 2021 12:03:56 +0200 Subject: [PATCH 251/402] fetching images should not kill the scanner --- .../Library/LibraryManager.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index c18838caf..494eb5929 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1914,12 +1914,17 @@ namespace Emby.Server.Implementations.Library } catch (ArgumentException) { - _logger.LogWarning("Cannot get image index for {0}", img.Path); + _logger.LogWarning("Cannot get image index for {ImagePath}", img.Path); continue; } catch (InvalidOperationException) { - _logger.LogWarning("Cannot fetch image from {0}", img.Path); + _logger.LogWarning("Cannot fetch image from {ImagePath}", img.Path); + continue; + } + catch (HttpRequestException ex) + { + _logger.LogWarning("Cannot fetch image from {ImagePath}. Http status code: {HttpStatus}", img.Path, ex.StatusCode); continue; } } @@ -1932,7 +1937,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path); + _logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path); image.Width = 0; image.Height = 0; continue; @@ -1944,7 +1949,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Cannot compute blurhash for {0}", image.Path); + _logger.LogError(ex, "Cannot compute blurhash for {ImagePath}", image.Path); image.BlurHash = string.Empty; } @@ -1954,7 +1959,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Cannot update DateModified for {0}", image.Path); + _logger.LogError(ex, "Cannot update DateModified for {ImagePath}", image.Path); } } From 08ccf2a49cc5c67026b324570a975aa9adab8915 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 13:20:12 +0200 Subject: [PATCH 252/402] Resolve name from episode folder --- .../Library/LibraryManager.cs | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index c18838caf..f92c30093 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -27,6 +27,7 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -48,6 +49,7 @@ using MediaBrowser.Providers.MediaInfo; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; +using EpisodeInfo = Emby.Naming.TV.EpisodeInfo; using Genre = MediaBrowser.Controller.Entities.Genre; using Person = MediaBrowser.Controller.Entities.Person; using VideoResolver = Emby.Naming.Video.VideoResolver; @@ -2512,7 +2514,7 @@ namespace Emby.Server.Implementations.Library public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh) { var series = episode.Series; - bool? isAbsoluteNaming = series == null ? false : string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase); + bool? isAbsoluteNaming = series != null && string.Equals(series.DisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase); if (!isAbsoluteNaming.Value) { // In other words, no filter applied @@ -2524,9 +2526,32 @@ namespace Emby.Server.Implementations.Library var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd; // TODO nullable - what are we trying to do there with empty episodeInfo? - var episodeInfo = episode.IsFileProtocol - ? resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new Naming.TV.EpisodeInfo(episode.Path) - : new Naming.TV.EpisodeInfo(episode.Path); + EpisodeInfo episodeInfo = null; + if (episode.IsFileProtocol) + { + episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new EpisodeInfo(episode.Path); + // Resolve from parent folder if it's not the Season folder + if (!episodeInfo.EpisodeNumber.HasValue && episode.Parent is not Season) + { + var episodeInfoFromFolder = resolver.Resolve(Path.GetDirectoryName(episode.Path)!, true, null, null, isAbsoluteNaming); + // merge the missing information + episodeInfo.SeriesName = episodeInfoFromFolder?.SeriesName; + episodeInfo.EpisodeNumber ??= episodeInfoFromFolder?.EpisodeNumber; + episodeInfo.EndingEpisodeNumber ??= episodeInfoFromFolder?.EndingEpisodeNumber; + episodeInfo.SeasonNumber ??= episodeInfoFromFolder?.SeasonNumber; + episodeInfo.Container ??= episodeInfoFromFolder?.Container; + episodeInfo.Format3D ??= episodeInfoFromFolder?.Format3D; + episodeInfo.Is3D = episodeInfoFromFolder?.Is3D ?? episodeInfo.Is3D; + episodeInfo.IsStub = episodeInfoFromFolder?.IsStub ?? episodeInfo.IsStub; + episodeInfo.StubType = episodeInfoFromFolder?.StubType; + episodeInfo.IsByDate = episodeInfoFromFolder?.IsStub ?? episodeInfo.IsByDate; + episodeInfo.Day ??= episodeInfoFromFolder?.Day; + episodeInfo.Month ??= episodeInfoFromFolder?.Month; + episodeInfo.Year ??= episodeInfoFromFolder?.Year; + } + } + + episodeInfo ??= new EpisodeInfo(episode.Path); try { From e7fc18d0f31c5516889c9dbb65d3cb3421d8b271 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 13:28:27 +0200 Subject: [PATCH 253/402] Fix type check --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f92c30093..c69b8991b 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2531,7 +2531,7 @@ namespace Emby.Server.Implementations.Library { episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new EpisodeInfo(episode.Path); // Resolve from parent folder if it's not the Season folder - if (!episodeInfo.EpisodeNumber.HasValue && episode.Parent is not Season) + if (!episodeInfo.EpisodeNumber.HasValue && episode.Parent.GetType() == typeof(Folder)) { var episodeInfoFromFolder = resolver.Resolve(Path.GetDirectoryName(episode.Path)!, true, null, null, isAbsoluteNaming); // merge the missing information From 69d2368fbcf734f48341edf73a9e8baae6229343 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 13:31:17 +0200 Subject: [PATCH 254/402] Copy paste error --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index c69b8991b..aed098e1b 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2544,7 +2544,7 @@ namespace Emby.Server.Implementations.Library episodeInfo.Is3D = episodeInfoFromFolder?.Is3D ?? episodeInfo.Is3D; episodeInfo.IsStub = episodeInfoFromFolder?.IsStub ?? episodeInfo.IsStub; episodeInfo.StubType = episodeInfoFromFolder?.StubType; - episodeInfo.IsByDate = episodeInfoFromFolder?.IsStub ?? episodeInfo.IsByDate; + episodeInfo.IsByDate = episodeInfoFromFolder?.IsByDate ?? episodeInfo.IsByDate; episodeInfo.Day ??= episodeInfoFromFolder?.Day; episodeInfo.Month ??= episodeInfoFromFolder?.Month; episodeInfo.Year ??= episodeInfoFromFolder?.Year; From 457229c56de2bf13af8852611a9c47c4950f1561 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 13:43:40 +0200 Subject: [PATCH 255/402] Simplification --- Emby.Naming/TV/EpisodeResolver.cs | 5 ++++ .../Library/LibraryManager.cs | 25 ++++++------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index f7df58786..eac192be5 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -68,6 +68,11 @@ namespace Emby.Naming.TV var parsingResult = new EpisodePathParser(_options) .Parse(path, isDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo); + if (!parsingResult.Success) + { + return null; + } + return new EpisodeInfo(path) { Container = container, diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index aed098e1b..f840f3695 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2529,25 +2529,16 @@ namespace Emby.Server.Implementations.Library EpisodeInfo episodeInfo = null; if (episode.IsFileProtocol) { - episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new EpisodeInfo(episode.Path); + episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming); // Resolve from parent folder if it's not the Season folder - if (!episodeInfo.EpisodeNumber.HasValue && episode.Parent.GetType() == typeof(Folder)) + if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder)) { - var episodeInfoFromFolder = resolver.Resolve(Path.GetDirectoryName(episode.Path)!, true, null, null, isAbsoluteNaming); - // merge the missing information - episodeInfo.SeriesName = episodeInfoFromFolder?.SeriesName; - episodeInfo.EpisodeNumber ??= episodeInfoFromFolder?.EpisodeNumber; - episodeInfo.EndingEpisodeNumber ??= episodeInfoFromFolder?.EndingEpisodeNumber; - episodeInfo.SeasonNumber ??= episodeInfoFromFolder?.SeasonNumber; - episodeInfo.Container ??= episodeInfoFromFolder?.Container; - episodeInfo.Format3D ??= episodeInfoFromFolder?.Format3D; - episodeInfo.Is3D = episodeInfoFromFolder?.Is3D ?? episodeInfo.Is3D; - episodeInfo.IsStub = episodeInfoFromFolder?.IsStub ?? episodeInfo.IsStub; - episodeInfo.StubType = episodeInfoFromFolder?.StubType; - episodeInfo.IsByDate = episodeInfoFromFolder?.IsByDate ?? episodeInfo.IsByDate; - episodeInfo.Day ??= episodeInfoFromFolder?.Day; - episodeInfo.Month ??= episodeInfoFromFolder?.Month; - episodeInfo.Year ??= episodeInfoFromFolder?.Year; + episodeInfo = resolver.Resolve(Path.GetDirectoryName(episode.Path)!, true, null, null, isAbsoluteNaming); + if (episodeInfo != null) + { + // add the container + episodeInfo.Container = Path.GetExtension(episode.Path)?.TrimStart('.'); + } } } From 53db1a1ffcba7edf9e5da677b6ed7e7e67f0d63a Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 13:47:47 +0200 Subject: [PATCH 256/402] ... --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f840f3695..dd678eca0 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2533,7 +2533,7 @@ namespace Emby.Server.Implementations.Library // Resolve from parent folder if it's not the Season folder if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder)) { - episodeInfo = resolver.Resolve(Path.GetDirectoryName(episode.Path)!, true, null, null, isAbsoluteNaming); + episodeInfo = resolver.Resolve(episode.Parent.Path, true, null, null, isAbsoluteNaming); if (episodeInfo != null) { // add the container From 5c4be2416d89bf31c6f62042f833e8f91621d8f6 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 13:48:25 +0200 Subject: [PATCH 257/402] Remove unused import --- Emby.Server.Implementations/Library/LibraryManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index dd678eca0..ac5028827 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -27,7 +27,6 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; From 381db60ed37c8d1fa56d57f9e63b750949ad974b Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 15:05:39 +0200 Subject: [PATCH 258/402] fix test --- Emby.Naming/TV/EpisodeResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index eac192be5..c63aec64e 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -68,7 +68,7 @@ namespace Emby.Naming.TV var parsingResult = new EpisodePathParser(_options) .Parse(path, isDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo); - if (!parsingResult.Success) + if (!parsingResult.Success && !isStub) { return null; } From ce4f73022116bb7910d7a73daf69f7a0624b2437 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Fri, 9 Apr 2021 20:02:23 +0400 Subject: [PATCH 259/402] Add support for TMDB series absolute and DVD order --- .../MediaBrowser.Providers.csproj | 2 +- .../Tmdb/TV/TmdbEpisodeImageProvider.cs | 2 +- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 2 +- .../Plugins/Tmdb/TmdbClientManager.cs | 69 ++++++++++++++++++- 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 152ea664a..de7860b2e 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -22,7 +22,7 @@ - + diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index d92336624..ba18c542f 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -65,7 +65,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var episodeResult = await _tmdbClientManager - .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, null, null, cancellationToken) + .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, series.DisplayOrder, null, null, cancellationToken) .ConfigureAwait(false); var stills = episodeResult?.Images?.Stills; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index b455e5634..36e7fe91a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } var episodeResult = await _tmdbClientManager - .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, info.SeriesDisplayOrder, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) .ConfigureAwait(false); if (episodeResult == null) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index bf0f027fc..3aa673274 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -125,7 +125,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb tmdbId, language: TmdbUtils.NormalizeLanguage(language), includeImageLanguage: imageLanguages, - extraMethods: TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.Keywords | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings, + extraMethods: TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.Keywords | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups, cancellationToken: cancellationToken).ConfigureAwait(false); if (series != null) @@ -136,6 +136,56 @@ namespace MediaBrowser.Providers.Plugins.Tmdb return series; } + /// + /// Gets a tv show episode group from the TMDb API based on the show id and the display order. + /// + /// The tv show's TMDb id. + /// The display order. + /// The tv show's language. + /// A comma-separated list of image languages. + /// The cancellation token. + /// The TMDb tv show episode group information or null if not found. + public async Task GetSeriesGroupAsync(int tvShowId, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken) + { + var key = $"group-{tvShowId.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}"; + if (_memoryCache.TryGetValue(key, out TvGroupCollection group)) + { + return group; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + TvGroupType? groupType = + displayOrder == "absolute" ? TvGroupType.Absolute : + displayOrder == "dvd" ? TvGroupType.DVD : + null; + + if (groupType == null) + { + return null; + } + + var series = await GetSeriesAsync(tvShowId, language, imageLanguages, cancellationToken); + var episodeGroupId = series.EpisodeGroups.Results.Find(g => g.Type == groupType)?.Id; + + if (episodeGroupId == null) + { + return null; + } + + group = await _tmDbClient.GetTvEpisodeGroupsAsync( + episodeGroupId, + language: TmdbUtils.NormalizeLanguage(language), + cancellationToken: cancellationToken).ConfigureAwait(false); + + if (group != null) + { + _memoryCache.Set(key, group, TimeSpan.FromHours(CacheDurationInHours)); + } + + return group; + } + /// /// Gets a tv season from the TMDb API based on the tv show's TMDb id. /// @@ -177,13 +227,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// The tv show's TMDb id. /// The season number. /// The episode number. + /// The display order. /// The episode's language. /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb tv episode information or null if not found. - public async Task GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string language, string imageLanguages, CancellationToken cancellationToken) + public async Task GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken) { - var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{language}"; + var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}"; if (_memoryCache.TryGetValue(key, out TvEpisode episode)) { return episode; @@ -191,6 +242,18 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); + var group = await GetSeriesGroupAsync(tvShowId, displayOrder, language, imageLanguages, cancellationToken); + if (group != null) + { + var season = group.Groups.Find(s => s.Order == seasonNumber); + var ep = season?.Episodes.Find(e => e.Order == episodeNumber - 1); + if (ep != null) + { + seasonNumber = ep.SeasonNumber; + episodeNumber = ep.EpisodeNumber; + } + } + episode = await _tmDbClient.GetTvEpisodeAsync( tvShowId, seasonNumber, From 1a3352003d0736d4d18513fb0055ae3b6452cded Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 23:02:36 +0200 Subject: [PATCH 260/402] don't die on dangling symlinks --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 679795dd2..7c9b7c2d0 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -249,9 +249,18 @@ namespace Emby.Server.Implementations.IO // Issue #2354 get the size of files behind symbolic links if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)) { - using (Stream thisFileStream = File.OpenRead(fileInfo.FullName)) + try { - result.Length = thisFileStream.Length; + using (Stream thisFileStream = File.OpenRead(fileInfo.FullName)) + { + result.Length = thisFileStream.Length; + } + } + catch (FileNotFoundException ex) + { + // Dangling symlinks cannot be detected before opening the file unfortunately... + Logger.LogError("Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", ex); + result.Exists = false; } } From f2cba352e58f5d52693875365fdd400ad7c11863 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 9 Apr 2021 23:30:07 +0200 Subject: [PATCH 261/402] catch ioexception and include stack trace --- Emby.Server.Implementations/Library/LibraryManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 494eb5929..7f29e3e99 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1917,14 +1917,14 @@ namespace Emby.Server.Implementations.Library _logger.LogWarning("Cannot get image index for {ImagePath}", img.Path); continue; } - catch (InvalidOperationException) + catch (Exception ex) when (ex is InvalidOperationException || ex is IOException) { - _logger.LogWarning("Cannot fetch image from {ImagePath}", img.Path); + _logger.LogWarning(ex, "Cannot fetch image from {ImagePath}", img.Path); continue; } catch (HttpRequestException ex) { - _logger.LogWarning("Cannot fetch image from {ImagePath}. Http status code: {HttpStatus}", img.Path, ex.StatusCode); + _logger.LogWarning(ex, "Cannot fetch image from {ImagePath}. Http status code: {HttpStatus}", img.Path, ex.StatusCode); continue; } } From ef527df28f1a48f31aa4bbab36c9443075b68084 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 10 Apr 2021 00:06:48 +0200 Subject: [PATCH 262/402] Set mediatype to Audio for playlists in a music library --- .../Library/Resolvers/PlaylistResolver.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs index c76d41e5c..5f051321f 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs @@ -63,7 +63,8 @@ namespace Emby.Server.Implementations.Library.Resolvers { Path = args.Path, Name = Path.GetFileNameWithoutExtension(args.Path), - IsInMixedFolder = true + IsInMixedFolder = true, + PlaylistMediaType = MediaType.Audio }; } } From f99237cf9b7c2c33c18fefafe79d50b5b414781f Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sat, 10 Apr 2021 00:16:37 +0200 Subject: [PATCH 263/402] Update Emby.Server.Implementations/IO/ManagedFileSystem.cs Co-authored-by: Cody Robibero --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 7c9b7c2d0..3893a1577 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -259,7 +259,7 @@ namespace Emby.Server.Implementations.IO catch (FileNotFoundException ex) { // Dangling symlinks cannot be detected before opening the file unfortunately... - Logger.LogError("Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", ex); + Logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", fileInfo.FullName); result.Exists = false; } } From 321e383965cf593991c0cbd4e9367cce4e408dc2 Mon Sep 17 00:00:00 2001 From: Ian Walton Date: Fri, 9 Apr 2021 19:22:22 -0400 Subject: [PATCH 264/402] Fix setting audio stream in PlaybackInfo for jellyfin-web. --- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index a2d0e7030..f07271821 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -283,6 +283,7 @@ namespace Jellyfin.Api.Helpers if (streamInfo != null) { SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex; } } } @@ -327,6 +328,7 @@ namespace Jellyfin.Api.Helpers if (streamInfo != null) { SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex; } } } @@ -354,6 +356,7 @@ namespace Jellyfin.Api.Helpers // Do this after the above so that StartPositionTicks is set SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex; } } else @@ -391,6 +394,7 @@ namespace Jellyfin.Api.Helpers // Do this after the above so that StartPositionTicks is set SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex; } } } From 117736aac9d0267c3a1a26192d564fc4e5541fe5 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 10 Apr 2021 17:09:45 +0200 Subject: [PATCH 265/402] Fix LogUnmatchedProfile formatting --- Emby.Dlna/DlnaManager.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index c94d803e1..3417076dc 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -126,14 +126,14 @@ namespace Emby.Dlna var builder = new StringBuilder(); builder.AppendLine("No matching device profile found. The default will need to be used."); - builder.Append("FriendlyName:").AppendLine(profile.FriendlyName); - builder.Append("Manufacturer:").AppendLine(profile.Manufacturer); - builder.Append("ManufacturerUrl:").AppendLine(profile.ManufacturerUrl); - builder.Append("ModelDescription:").AppendLine(profile.ModelDescription); - builder.Append("ModelName:").AppendLine(profile.ModelName); - builder.Append("ModelNumber:").AppendLine(profile.ModelNumber); - builder.Append("ModelUrl:").AppendLine(profile.ModelUrl); - builder.Append("SerialNumber:").AppendLine(profile.SerialNumber); + builder.Append("FriendlyName: ").AppendLine(profile.FriendlyName); + builder.Append("Manufacturer: ").AppendLine(profile.Manufacturer); + builder.Append("ManufacturerUrl: ").AppendLine(profile.ManufacturerUrl); + builder.Append("ModelDescription: ").AppendLine(profile.ModelDescription); + builder.Append("ModelName: ").AppendLine(profile.ModelName); + builder.Append("ModelNumber: ").AppendLine(profile.ModelNumber); + builder.Append("ModelUrl: ").AppendLine(profile.ModelUrl); + builder.Append("SerialNumber: ").AppendLine(profile.SerialNumber); _logger.LogInformation(builder.ToString()); } From 1fe26fe352a854523337842b16d6d0be5e68be5a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 10 Apr 2021 19:44:09 +0100 Subject: [PATCH 266/402] Work through dns failure on test. --- tests/Jellyfin.Networking.Tests/NetworkParseTests.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index caea0d265..b6bbfc5e1 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -382,6 +382,8 @@ namespace Jellyfin.Networking.Tests [InlineData("jellyfin.org", "eth16", false, "eth16")] // User on external network, no binding - so result is the 1st external. [InlineData("jellyfin.org", "", false, "eth11")] + // Dns failure - should skip the test. + [InlineData("ospoakdposkd.abc", "", false, "eth11")] // User assumed to be internal, no binding - so result is the 1st internal. [InlineData("", "", false, "eth16")] public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result) @@ -414,10 +416,13 @@ namespace Jellyfin.Networking.Tests _ = nm.TryParseInterface(result, out Collection? resultObj); - if (resultObj != null) + // Check to see if dns resolution is working. If not, skip test. + _ = IPHost.TryParse(source, out var host); + + if (resultObj != null && host?.HasAddress == true) { result = ((IPNetAddress)resultObj[0]).ToString(true); - var intf = nm.GetBindInterface(source, out int? _); + var intf = nm.GetBindInterface(source, out _); Assert.Equal(intf, result); } From be9cb7af2c38c41e466b565f39109489fdfb61aa Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 10 Apr 2021 22:42:09 +0200 Subject: [PATCH 267/402] Minor improvements to IPObjects --- MediaBrowser.Common/Net/IPHost.cs | 8 ++++---- MediaBrowser.Common/Net/IPNetAddress.cs | 16 ++++++++-------- MediaBrowser.Common/Net/IPObject.cs | 16 +++------------- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index d67b6b8e1..fb3ef9b12 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -135,7 +135,7 @@ namespace MediaBrowser.Common.Net } // See if it's an IPv6 with port address e.g. [::1] or [::1]:120. - int i = host.IndexOf("]", StringComparison.OrdinalIgnoreCase); + int i = host.IndexOf(']', StringComparison.Ordinal); if (i != -1) { return TryParse(host.Remove(i - 1).TrimStart(' ', '['), out hostObj); @@ -389,8 +389,8 @@ namespace MediaBrowser.Common.Net /// protected override IPObject CalculateNetworkAddress() { - var netAddr = NetworkAddressOf(this[0], PrefixLength); - return new IPNetAddress(netAddr.Address, netAddr.PrefixLength); + var (address, prefixLength) = NetworkAddressOf(this[0], PrefixLength); + return new IPNetAddress(address, prefixLength); } /// @@ -427,7 +427,7 @@ namespace MediaBrowser.Common.Net // Resolves the host name - so save a DNS lookup. if (string.Equals(HostName, "localhost", StringComparison.OrdinalIgnoreCase)) { - _addresses = new IPAddress[] { new IPAddress(Ipv4Loopback), new IPAddress(Ipv6Loopback) }; + _addresses = new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback }; return; } diff --git a/MediaBrowser.Common/Net/IPNetAddress.cs b/MediaBrowser.Common/Net/IPNetAddress.cs index 59e37a5c6..589aad4b0 100644 --- a/MediaBrowser.Common/Net/IPNetAddress.cs +++ b/MediaBrowser.Common/Net/IPNetAddress.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Common.Net /// /// IP6Loopback address host. /// - public static readonly IPNetAddress IP6Loopback = IPNetAddress.Parse("::1"); + public static readonly IPNetAddress IP6Loopback = new IPNetAddress(IPAddress.IPv6Loopback); /// /// Object's IP address. @@ -113,7 +113,7 @@ namespace MediaBrowser.Common.Net } // Is it a network? - string[] tokens = addr.Split("/"); + string[] tokens = addr.Split('/'); if (tokens.Length == 2) { @@ -171,8 +171,8 @@ namespace MediaBrowser.Common.Net address = address.MapToIPv4(); } - var altAddress = NetworkAddressOf(address, PrefixLength); - return NetworkAddress.Address.Equals(altAddress.Address) && NetworkAddress.PrefixLength >= altAddress.PrefixLength; + var (altAddress, altPrefix) = NetworkAddressOf(address, PrefixLength); + return NetworkAddress.Address.Equals(altAddress) && NetworkAddress.PrefixLength >= altPrefix; } /// @@ -196,8 +196,8 @@ namespace MediaBrowser.Common.Net return NetworkAddress.PrefixLength <= netaddrObj.PrefixLength; } - var altAddress = NetworkAddressOf(netaddrObj.Address, PrefixLength); - return NetworkAddress.Address.Equals(altAddress.Address); + var altAddress = NetworkAddressOf(netaddrObj.Address, PrefixLength).address; + return NetworkAddress.Address.Equals(altAddress); } return false; @@ -270,8 +270,8 @@ namespace MediaBrowser.Common.Net /// protected override IPObject CalculateNetworkAddress() { - var value = NetworkAddressOf(_address, PrefixLength); - return new IPNetAddress(value.Address, value.PrefixLength); + var (address, prefixLength) = NetworkAddressOf(_address, PrefixLength); + return new IPNetAddress(address, prefixLength); } } } diff --git a/MediaBrowser.Common/Net/IPObject.cs b/MediaBrowser.Common/Net/IPObject.cs index 69cd57f8a..3542dcd75 100644 --- a/MediaBrowser.Common/Net/IPObject.cs +++ b/MediaBrowser.Common/Net/IPObject.cs @@ -10,16 +10,6 @@ namespace MediaBrowser.Common.Net /// public abstract class IPObject : IEquatable { - /// - /// IPv6 Loopback address. - /// - protected static readonly byte[] Ipv6Loopback = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; - - /// - /// IPv4 Loopback address. - /// - protected static readonly byte[] Ipv4Loopback = { 127, 0, 0, 1 }; - /// /// The network address of this object. /// @@ -64,7 +54,7 @@ namespace MediaBrowser.Common.Net /// IP Address to convert. /// Subnet prefix. /// IPAddress. - public static (IPAddress Address, byte PrefixLength) NetworkAddressOf(IPAddress address, byte prefixLength) + public static (IPAddress address, byte prefixLength) NetworkAddressOf(IPAddress address, byte prefixLength) { if (address == null) { @@ -78,7 +68,7 @@ namespace MediaBrowser.Common.Net if (IsLoopback(address)) { - return (Address: address, PrefixLength: prefixLength); + return (address, prefixLength); } // An ip address is just a list of bytes, each one representing a segment on the network. @@ -110,7 +100,7 @@ namespace MediaBrowser.Common.Net } // Return the network address for the prefix. - return (Address: new IPAddress(addressBytes), PrefixLength: prefixLength); + return (new IPAddress(addressBytes), prefixLength); } /// From f2e74917550e8c5d532d180d01eeaa01d92a6cf2 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 10 Apr 2021 22:58:32 +0200 Subject: [PATCH 268/402] Do not check permissions for Folders collectiontype --- Jellyfin.Api/Controllers/ItemsController.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 2c9760f6d..74cf3b162 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -246,8 +246,13 @@ namespace Jellyfin.Api.Controllers folder = _libraryManager.GetUserRootFolder(); } - if (folder is IHasCollectionType hasCollectionType - && string.Equals(hasCollectionType.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) + string? collectionType = null; + if (folder is IHasCollectionType hasCollectionType) + { + collectionType = hasCollectionType.CollectionType; + } + + if (string.Equals(collectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) { recursive = true; includeItemTypes = new[] { BaseItemKind.Playlist }; @@ -270,10 +275,11 @@ namespace Jellyfin.Api.Controllers } } - if (!(item is UserRootFolder) + if (item is not UserRootFolder && !isInEnabledFolder && !user.HasPermission(PermissionKind.EnableAllFolders) - && !user.HasPermission(PermissionKind.EnableAllChannels)) + && !user.HasPermission(PermissionKind.EnableAllChannels) + && !string.Equals(collectionType, CollectionType.Folders, StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name); return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}."); From 42bcf171d98276cd8472a8400e6bd960f475e19b Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 10 Apr 2021 23:54:35 +0200 Subject: [PATCH 269/402] Use sync Serialize when writing scheduled tasks to disk --- .../ScheduledTasks/ScheduledTaskWorker.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index a145a8423..3cc2cefb9 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -178,7 +178,8 @@ namespace Emby.Server.Implementations.ScheduledTasks lock (_lastExecutionResultSyncLock) { using FileStream createStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); - JsonSerializer.SerializeAsync(createStream, value, _jsonOptions); + using Utf8JsonWriter jsonStream = new Utf8JsonWriter(createStream); + JsonSerializer.Serialize(jsonStream, value, _jsonOptions); } } } @@ -578,7 +579,8 @@ namespace Emby.Server.Implementations.ScheduledTasks Directory.CreateDirectory(Path.GetDirectoryName(path)); using FileStream createStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); - JsonSerializer.SerializeAsync(createStream, triggers, _jsonOptions); + using Utf8JsonWriter jsonWriter = new Utf8JsonWriter(createStream); + JsonSerializer.Serialize(jsonWriter, triggers, _jsonOptions); } /// From 35cfd760d41a27676dfbe9f215ba913d9d71451b Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 11 Apr 2021 00:27:53 +0200 Subject: [PATCH 270/402] Do not touch "old" local artwork unless saving locally --- MediaBrowser.Providers/Manager/ImageSaver.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 5bb85be7b..fb1d4f490 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -172,7 +172,9 @@ namespace MediaBrowser.Providers.Manager SetImagePath(item, type, imageIndex, savedPaths[0]); // Delete the current path - if (currentImageIsLocalFile && !savedPaths.Contains(currentImagePath, StringComparer.OrdinalIgnoreCase)) + if (currentImageIsLocalFile + && !savedPaths.Contains(currentImagePath, StringComparer.OrdinalIgnoreCase) + && (saveLocally || currentImagePath.Contains(_config.ApplicationPaths.InternalMetadataPath, StringComparison.OrdinalIgnoreCase))) { var currentPath = currentImagePath; From 5fc664fd4f8e600887a4220abb366b75cc92ac01 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 11 Apr 2021 00:35:32 +0200 Subject: [PATCH 271/402] Add test for handling dangling symlinks --- jellyfin.ruleset | 2 ++ .../IO/ManagedFileSystemTests.cs | 25 +++++++++++++++++++ ...llyfin.Server.Implementations.Tests.csproj | 1 + 3 files changed, 28 insertions(+) diff --git a/jellyfin.ruleset b/jellyfin.ruleset index b012d2b00..19c0a08b2 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -78,5 +78,7 @@ + + diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 671c59b2e..5a535ac51 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -1,3 +1,7 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.InteropServices; using AutoFixture; using AutoFixture.AutoMoq; using Emby.Server.Implementations.IO; @@ -38,5 +42,26 @@ namespace Jellyfin.Server.Implementations.Tests.IO Assert.Equal(expectedAbsolutePath, generatedPath); } } + + [SkippableFact] + public void GetFileInfo_DanglingSymlink_ExistsFalse() + { + Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + + string testFileDir = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); + string testFileName = Path.Combine(testFileDir, Path.GetRandomFileName() + "-danglingsym.link"); + + Directory.CreateDirectory(testFileDir); + Assert.Equal(0, symlink("thispathdoesntexist", testFileName)); + Assert.True(File.Exists(testFileName)); + + var metadata = _sut.GetFileInfo(testFileName); + Assert.False(metadata.Exists); + } + + [SuppressMessage("Naming Rules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Have to")] + [DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)] + [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)] + private static extern int symlink(string target, string linkpath); } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index fe26ec3ae..ee59dad5a 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -28,6 +28,7 @@ + From 383aa4e4d9cf4b0997b9277df838728102e0db3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Wed, 17 Mar 2021 00:15:09 +0100 Subject: [PATCH 272/402] Add Resize to fill box alternative to image endpoints --- Jellyfin.Api/Controllers/ImageController.cs | 124 +++++++++++++++--- .../Drawing/ImageHelper.cs | 2 +- .../Drawing/ImageProcessingOptions.cs | 4 + MediaBrowser.Model/Drawing/DrawingUtils.cs | 48 +++++++ 4 files changed, 161 insertions(+), 17 deletions(-) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 2119e8e2c..1da567793 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -490,6 +490,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Supply the cache tag from the item object to receive strong caching headers. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. The of the returned image. @@ -528,7 +530,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex) + [FromQuery] int? imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -549,6 +553,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -570,6 +576,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Supply the cache tag from the item object to receive strong caching headers. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. The of the returned image. @@ -607,7 +615,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? unplayedCount, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer) + [FromQuery] string? foregroundLayer, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -628,6 +638,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -648,6 +660,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Supply the cache tag from the item object to receive strong caching headers. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Determines the output format of the image - original,gif,jpg,png. @@ -686,7 +700,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute, Required] int imageIndex) + [FromRoute, Required] int imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -707,6 +723,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -731,6 +749,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -765,7 +785,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute, Required] int imageIndex) + [FromRoute, Required] int imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetArtist(name); if (item == null) @@ -786,6 +808,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -810,6 +834,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -844,7 +870,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex) + [FromQuery] int? imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetGenre(name); if (item == null) @@ -865,6 +893,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -890,6 +920,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -923,7 +955,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer) + [FromQuery] string? foregroundLayer, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetGenre(name); if (item == null) @@ -944,6 +978,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -968,6 +1004,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1002,7 +1040,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex) + [FromQuery] int? imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetMusicGenre(name); if (item == null) @@ -1023,6 +1063,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1048,6 +1090,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1081,7 +1125,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer) + [FromQuery] string? foregroundLayer, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetMusicGenre(name); if (item == null) @@ -1102,6 +1148,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1126,6 +1174,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1160,7 +1210,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex) + [FromQuery] int? imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetPerson(name); if (item == null) @@ -1181,6 +1233,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1206,6 +1260,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1239,7 +1295,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer) + [FromQuery] string? foregroundLayer, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetPerson(name); if (item == null) @@ -1260,6 +1318,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1284,6 +1344,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1318,7 +1380,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex) + [FromQuery] int? imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetStudio(name); if (item == null) @@ -1339,6 +1403,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1364,6 +1430,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1397,7 +1465,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer) + [FromQuery] string? foregroundLayer, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var item = _libraryManager.GetStudio(name); if (item == null) @@ -1418,6 +1488,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1442,6 +1514,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1476,7 +1550,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex) + [FromQuery] int? imageIndex, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var user = _userManager.GetUserById(userId); if (user?.ProfileImage == null) @@ -1514,6 +1590,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1540,6 +1618,8 @@ namespace Jellyfin.Api.Controllers /// The fixed image width to return. /// The fixed image height to return. /// Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases. + /// Width of box to fill. + /// Height of box to fill. /// Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art. /// Optional. Add a played indicator. /// Optional. Blur image. @@ -1573,7 +1653,9 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer) + [FromQuery] string? foregroundLayer, + [FromQuery] int? fillHeight, + [FromQuery] int? fillWidth) { var user = _userManager.GetUserById(userId); if (user?.ProfileImage == null) @@ -1611,6 +1693,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, + fillHeight, + fillWidth, cropWhitespace, addPlayedIndicator, blur, @@ -1695,6 +1779,8 @@ namespace Jellyfin.Api.Controllers int? width, int? height, int? quality, + int? fillHeight, + int? fillWidth, bool? cropWhitespace, // TODO: Remove bool? addPlayedIndicator, int? blur, @@ -1773,7 +1859,9 @@ namespace Jellyfin.Api.Controllers outputFormats, cacheDuration, responseHeaders, - isHeadRequest).ConfigureAwait(false); + isHeadRequest, + fillHeight, + fillWidth).ConfigureAwait(false); } private ImageFormat[] GetOutputFormats(ImageFormat? format) @@ -1871,7 +1959,9 @@ namespace Jellyfin.Api.Controllers IReadOnlyCollection supportedFormats, TimeSpan? cacheDuration, IDictionary headers, - bool isHeadRequest) + bool isHeadRequest, + int? fillHeight, + int? fillWidth) { if (!imageInfo.IsLocalFile && item != null) { @@ -1887,6 +1977,8 @@ namespace Jellyfin.Api.Controllers ItemId = itemId, MaxHeight = maxHeight, MaxWidth = maxWidth, + FillHeight = fillHeight, + FillWidth = fillWidth, Quality = quality ?? 100, Width = width, AddPlayedIndicator = addPlayedIndicator ?? false, diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index 87c28d577..740726119 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Drawing { // Determine the output size based on incoming parameters var newSize = DrawingUtils.Resize(originalImageSize.Value, options.Width ?? 0, options.Height ?? 0, options.MaxWidth ?? 0, options.MaxHeight ?? 0); - + newSize = DrawingUtils.ResizeFill(newSize, options.FillWidth, options.FillHeight); return newSize; } diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 22de9a43e..4befb29c4 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -32,6 +32,10 @@ namespace MediaBrowser.Controller.Drawing public int? MaxHeight { get; set; } + public int? FillWidth { get; set; } + + public int? FillHeight { get; set; } + public int Quality { get; set; } public IReadOnlyCollection SupportedOutputFormats { get; set; } diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs index 1512c5233..8fb9bdc0a 100644 --- a/MediaBrowser.Model/Drawing/DrawingUtils.cs +++ b/MediaBrowser.Model/Drawing/DrawingUtils.cs @@ -57,6 +57,54 @@ namespace MediaBrowser.Model.Drawing return new ImageDimensions(newWidth, newHeight); } + /// + /// Resizes to fill box. + /// Returns original size if both width and height are null or zero. + /// + /// The original size object. + /// A new fixed width, if desired. + /// A new fixed height, if desired. + /// A new size object or size. + public static ImageDimensions ResizeFill( + ImageDimensions size, + int? fillWidth, + int? fillHeight) + { + // Return original size if input is invalid. + if ( + (fillWidth == null && fillHeight == null) + || (fillWidth == 0 || fillHeight == 0)) + { + return size; + } + + if (fillWidth == null || fillWidth == 0) + { + fillWidth = 1; + } + + if (fillHeight == null || fillHeight == 0) + { + fillHeight = 1; + } + + double widthRatio = (double)size.Width / (double)fillWidth; + double heightRatio = (double)size.Height / (double)fillHeight!; + // min() + double scaleRatio = widthRatio > heightRatio ? heightRatio : widthRatio; + + // Clamp to current size. + if (scaleRatio < 1) + { + return size; + } + + int newWidth = Convert.ToInt32(Math.Ceiling((double)size.Width / scaleRatio)); + int newHeight = Convert.ToInt32(Math.Ceiling((double)size.Height / scaleRatio)); + + return new ImageDimensions(newWidth, newHeight); + } + /// /// Gets the new width. /// From 13d0837b78011925d308e7ff593207b0e1cb330f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Wed, 17 Mar 2021 19:45:28 +0100 Subject: [PATCH 273/402] (mostly)Fix ResizeFill --- Jellyfin.Api/Controllers/ImageController.cs | 142 +++++++++--------- .../Drawing/ImageProcessingOptions.cs | 10 ++ MediaBrowser.Model/Drawing/DrawingUtils.cs | 12 +- 3 files changed, 86 insertions(+), 78 deletions(-) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 1da567793..55c2fe735 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -521,6 +521,8 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] string? tag, [FromQuery] bool? cropWhitespace, [FromQuery] ImageFormat? format, @@ -530,9 +532,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] int? imageIndex) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -553,8 +553,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -607,6 +607,8 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] string? tag, [FromQuery] bool? cropWhitespace, [FromQuery] ImageFormat? format, @@ -615,9 +617,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? unplayedCount, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] string? foregroundLayer) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -638,8 +638,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -691,6 +691,8 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromRoute, Required] string tag, [FromQuery] bool? cropWhitespace, [FromRoute, Required] ImageFormat format, @@ -700,9 +702,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute, Required] int imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromRoute, Required] int imageIndex) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -723,8 +723,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -780,14 +780,14 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute, Required] int imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromRoute, Required] int imageIndex) { var item = _libraryManager.GetArtist(name); if (item == null) @@ -808,8 +808,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -865,14 +865,14 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] int? imageIndex) { var item = _libraryManager.GetGenre(name); if (item == null) @@ -893,8 +893,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -951,13 +951,13 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] string? foregroundLayer) { var item = _libraryManager.GetGenre(name); if (item == null) @@ -978,8 +978,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1035,14 +1035,14 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] int? imageIndex) { var item = _libraryManager.GetMusicGenre(name); if (item == null) @@ -1063,8 +1063,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1121,13 +1121,13 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] string? foregroundLayer) { var item = _libraryManager.GetMusicGenre(name); if (item == null) @@ -1148,8 +1148,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1205,14 +1205,14 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] int? imageIndex) { var item = _libraryManager.GetPerson(name); if (item == null) @@ -1233,8 +1233,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1291,13 +1291,13 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] string? foregroundLayer) { var item = _libraryManager.GetPerson(name); if (item == null) @@ -1318,8 +1318,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1375,14 +1375,14 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] int? imageIndex) { var item = _libraryManager.GetStudio(name); if (item == null) @@ -1403,8 +1403,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1461,13 +1461,13 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] string? foregroundLayer) { var item = _libraryManager.GetStudio(name); if (item == null) @@ -1488,8 +1488,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1545,14 +1545,14 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromQuery] int? imageIndex, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] int? imageIndex) { var user = _userManager.GetUserById(userId); if (user?.ProfileImage == null) @@ -1590,8 +1590,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1649,13 +1649,13 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, + [FromQuery] int? fillWidth, + [FromQuery] int? fillHeight, [FromQuery] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, - [FromQuery] string? foregroundLayer, - [FromQuery] int? fillHeight, - [FromQuery] int? fillWidth) + [FromQuery] string? foregroundLayer) { var user = _userManager.GetUserById(userId); if (user?.ProfileImage == null) @@ -1693,8 +1693,8 @@ namespace Jellyfin.Api.Controllers width, height, quality, - fillHeight, fillWidth, + fillHeight, cropWhitespace, addPlayedIndicator, blur, @@ -1779,8 +1779,8 @@ namespace Jellyfin.Api.Controllers int? width, int? height, int? quality, - int? fillHeight, int? fillWidth, + int? fillHeight, bool? cropWhitespace, // TODO: Remove bool? addPlayedIndicator, int? blur, @@ -1844,11 +1844,13 @@ namespace Jellyfin.Api.Controllers item, itemId, imageIndex, - height, - maxHeight, - maxWidth, - quality, width, + height, + maxWidth, + maxHeight, + fillWidth, + fillHeight, + quality, addPlayedIndicator, percentPlayed, unplayedCount, @@ -1859,9 +1861,7 @@ namespace Jellyfin.Api.Controllers outputFormats, cacheDuration, responseHeaders, - isHeadRequest, - fillHeight, - fillWidth).ConfigureAwait(false); + isHeadRequest).ConfigureAwait(false); } private ImageFormat[] GetOutputFormats(ImageFormat? format) @@ -1944,11 +1944,13 @@ namespace Jellyfin.Api.Controllers BaseItem? item, Guid itemId, int? index, - int? height, - int? maxHeight, - int? maxWidth, - int? quality, int? width, + int? height, + int? maxWidth, + int? maxHeight, + int? fillWidth, + int? fillHeight, + int? quality, bool? addPlayedIndicator, double? percentPlayed, int? unplayedCount, @@ -1959,9 +1961,7 @@ namespace Jellyfin.Api.Controllers IReadOnlyCollection supportedFormats, TimeSpan? cacheDuration, IDictionary headers, - bool isHeadRequest, - int? fillHeight, - int? fillWidth) + bool isHeadRequest) { if (!imageInfo.IsLocalFile && item != null) { diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 4befb29c4..e1a2811ac 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -97,6 +97,16 @@ namespace MediaBrowser.Controller.Drawing return false; } + if (FillWidth.HasValue && sizeValue.Width > FillWidth.Value) + { + return false; + } + + if (FillHeight.HasValue && sizeValue.Height > FillHeight.Value) + { + return false; + } + return true; } diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs index 8fb9bdc0a..c2a144f81 100644 --- a/MediaBrowser.Model/Drawing/DrawingUtils.cs +++ b/MediaBrowser.Model/Drawing/DrawingUtils.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Model.Drawing } /// - /// Resizes to fill box. + /// Scale down to fill box. /// Returns original size if both width and height are null or zero. /// /// The original size object. @@ -71,9 +71,8 @@ namespace MediaBrowser.Model.Drawing int? fillHeight) { // Return original size if input is invalid. - if ( - (fillWidth == null && fillHeight == null) - || (fillWidth == 0 || fillHeight == 0)) + if ((fillWidth == null || fillWidth == 0) + && (fillHeight == null || fillHeight == 0)) { return size; } @@ -89,9 +88,8 @@ namespace MediaBrowser.Model.Drawing } double widthRatio = (double)size.Width / (double)fillWidth; - double heightRatio = (double)size.Height / (double)fillHeight!; - // min() - double scaleRatio = widthRatio > heightRatio ? heightRatio : widthRatio; + double heightRatio = (double)size.Height / (double)fillHeight; + double scaleRatio = Math.Min(widthRatio, heightRatio); // Clamp to current size. if (scaleRatio < 1) From e57c1655fb2b7d92db33ff3a3b6b09a09b69cf68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Wed, 17 Mar 2021 20:46:45 +0100 Subject: [PATCH 274/402] Remove GetSizeEstimate & GetEstimatedAspectRatio from MediaBrowser.Controller.Drawing.ImageHelper Rework GetCacheFilePath to take requested with and height parameters in stead of using estimated output size --- Emby.Drawing/ImageProcessor.cs | 112 +++++++++++++++--- .../Drawing/ImageHelper.cs | 66 +---------- 2 files changed, 101 insertions(+), 77 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 978173d5a..646620c43 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -171,11 +171,26 @@ namespace Emby.Drawing return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } - ImageDimensions newSize = ImageHelper.GetNewImageSize(options, null); int quality = options.Quality; ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency); - string cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer); + string cacheFilePath = GetCacheFilePath( + originalImagePath, + options.Width, + options.Height, + options.MaxWidth, + options.MaxHeight, + options.FillWidth, + options.FillHeight, + quality, + dateModified, + outputFormat, + options.AddPlayedIndicator, + options.PercentPlayed, + options.UnplayedCount, + options.Blur, + options.BackgroundColor, + options.ForegroundLayer); try { @@ -241,48 +256,111 @@ namespace Emby.Drawing /// /// Gets the cache file path based on a set of parameters. /// - private string GetCacheFilePath(string originalPath, ImageDimensions outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer) + private string GetCacheFilePath( + string originalPath, + int? width, + int? height, + int? maxWidth, + int? maxHeight, + int? fillWidth, + int? fillHeight, + int quality, + DateTime dateModified, + ImageFormat format, + bool addPlayedIndicator, + double percentPlayed, + int? unwatchedCount, + int? blur, + string backgroundColor, + string foregroundLayer) { - var filename = originalPath - + "width=" + outputSize.Width - + "height=" + outputSize.Height - + "quality=" + quality - + "datemodified=" + dateModified.Ticks - + "f=" + format; + System.Text.StringBuilder filename = new System.Text.StringBuilder(128); + filename.Append(originalPath); + + filename.Append(",quality="); + filename.Append(quality); + + filename.Append(",datemodified="); + filename.Append(dateModified.Ticks); + + filename.Append(",f="); + filename.Append(format); + + if (width.HasValue) + { + filename.Append(",width="); + filename.Append(width.Value); + } + + if (height.HasValue) + { + filename.Append(",height="); + filename.Append(height.Value); + } + + if (maxWidth.HasValue) + { + filename.Append(",maxwidth="); + filename.Append(maxWidth.Value); + } + + if (maxHeight.HasValue) + { + filename.Append(",maxheight="); + filename.Append(maxHeight.Value); + } + + if (fillWidth.HasValue) + { + filename.Append(",fillwidth="); + filename.Append(fillWidth.Value); + } + + if (fillHeight.HasValue) + { + filename.Append(",fillheight="); + filename.Append(fillHeight.Value); + } if (addPlayedIndicator) { - filename += "pl=true"; + filename.Append(",pl=true"); } if (percentPlayed > 0) { - filename += "p=" + percentPlayed; + filename.Append(",p="); + filename.Append(percentPlayed); } if (unwatchedCount.HasValue) { - filename += "p=" + unwatchedCount.Value; + filename.Append(",p="); + filename.Append(unwatchedCount.Value); } if (blur.HasValue) { - filename += "blur=" + blur.Value; + filename.Append(",blur="); + filename.Append(blur.Value); } if (!string.IsNullOrEmpty(backgroundColor)) { - filename += "b=" + backgroundColor; + filename.Append(",b="); + filename.Append(backgroundColor); } if (!string.IsNullOrEmpty(foregroundLayer)) { - filename += "fl=" + foregroundLayer; + filename.Append(",fl="); + filename.Append(foregroundLayer); } - filename += "v=" + Version; + filename.Append(",v="); + filename.Append(Version); - return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant()); + return GetCachePath(ResizedImageCachePath, filename.ToString(), "." + format.ToString().ToLowerInvariant()); } /// diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index 740726119..181f8e905 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#nullable enable using System; using MediaBrowser.Controller.Entities; @@ -9,67 +10,12 @@ namespace MediaBrowser.Controller.Drawing { public static class ImageHelper { - public static ImageDimensions GetNewImageSize(ImageProcessingOptions options, ImageDimensions? originalImageSize) + public static ImageDimensions GetNewImageSize(ImageProcessingOptions options, ImageDimensions originalImageSize) { - if (originalImageSize.HasValue) - { - // Determine the output size based on incoming parameters - var newSize = DrawingUtils.Resize(originalImageSize.Value, options.Width ?? 0, options.Height ?? 0, options.MaxWidth ?? 0, options.MaxHeight ?? 0); - newSize = DrawingUtils.ResizeFill(newSize, options.FillWidth, options.FillHeight); - return newSize; - } - - return GetSizeEstimate(options); - } - - private static ImageDimensions GetSizeEstimate(ImageProcessingOptions options) - { - if (options.Width.HasValue && options.Height.HasValue) - { - return new ImageDimensions(options.Width.Value, options.Height.Value); - } - - double aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item); - - int? width = options.Width ?? options.MaxWidth; - - if (width.HasValue) - { - int heightValue = Convert.ToInt32((double)width.Value / aspect); - return new ImageDimensions(width.Value, heightValue); - } - - var height = options.Height ?? options.MaxHeight ?? 200; - int widthValue = Convert.ToInt32(aspect * height); - return new ImageDimensions(widthValue, height); - } - - private static double GetEstimatedAspectRatio(ImageType type, BaseItem item) - { - switch (type) - { - case ImageType.Art: - case ImageType.Backdrop: - case ImageType.Chapter: - case ImageType.Screenshot: - case ImageType.Thumb: - return 1.78; - case ImageType.Banner: - return 5.4; - case ImageType.Box: - case ImageType.BoxRear: - case ImageType.Disc: - case ImageType.Menu: - case ImageType.Profile: - return 1; - case ImageType.Logo: - return 2.58; - case ImageType.Primary: - double defaultPrimaryImageAspectRatio = item.GetDefaultPrimaryImageAspectRatio(); - return defaultPrimaryImageAspectRatio > 0 ? defaultPrimaryImageAspectRatio : 2.0 / 3; - default: - return 1; - } + // Determine the output size based on incoming parameters + var newSize = DrawingUtils.Resize(originalImageSize, options.Width ?? 0, options.Height ?? 0, options.MaxWidth ?? 0, options.MaxHeight ?? 0); + newSize = DrawingUtils.ResizeFill(newSize, options.FillWidth, options.FillHeight); + return newSize; } } } From afff226514a6f645a6738f1e407208826b84f1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Thu, 18 Mar 2021 23:40:45 +0100 Subject: [PATCH 275/402] Apply suggestions from code review Co-authored-by: Cody Robibero --- MediaBrowser.Model/Drawing/DrawingUtils.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs index c2a144f81..556792768 100644 --- a/MediaBrowser.Model/Drawing/DrawingUtils.cs +++ b/MediaBrowser.Model/Drawing/DrawingUtils.cs @@ -87,8 +87,8 @@ namespace MediaBrowser.Model.Drawing fillHeight = 1; } - double widthRatio = (double)size.Width / (double)fillWidth; - double heightRatio = (double)size.Height / (double)fillHeight; + double widthRatio = size.Width / (double)fillWidth; + double heightRatio = size.Height / (double)fillHeight; double scaleRatio = Math.Min(widthRatio, heightRatio); // Clamp to current size. @@ -97,8 +97,8 @@ namespace MediaBrowser.Model.Drawing return size; } - int newWidth = Convert.ToInt32(Math.Ceiling((double)size.Width / scaleRatio)); - int newHeight = Convert.ToInt32(Math.Ceiling((double)size.Height / scaleRatio)); + int newWidth = Convert.ToInt32(Math.Ceiling(size.Width / scaleRatio)); + int newHeight = Convert.ToInt32(Math.Ceiling(size.Height / scaleRatio)); return new ImageDimensions(newWidth, newHeight); } From e0edbc5754ad4d6afad1f01874ca2085d4f0791d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Mon, 29 Mar 2021 00:46:19 +0200 Subject: [PATCH 276/402] Apply suggestions from code review Co-authored-by: Claus Vium --- Emby.Drawing/ImageProcessor.cs | 2 +- MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 646620c43..78b53f36a 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -274,7 +274,7 @@ namespace Emby.Drawing string backgroundColor, string foregroundLayer) { - System.Text.StringBuilder filename = new System.Text.StringBuilder(128); + var filename = new StringBuilder(128); filename.Append(originalPath); filename.Append(",quality="); diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index e1a2811ac..230a0af60 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -97,12 +97,7 @@ namespace MediaBrowser.Controller.Drawing return false; } - if (FillWidth.HasValue && sizeValue.Width > FillWidth.Value) - { - return false; - } - - if (FillHeight.HasValue && sizeValue.Height > FillHeight.Value) + if (sizeValue.Width > FillWidth || sizeValue.Height > FillHeight) { return false; } From d0857cf6553ffa9560b42720af04e0b5ebcc9c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Mon, 29 Mar 2021 00:48:59 +0200 Subject: [PATCH 277/402] Fix build, increase StringBuilder starting size --- Emby.Drawing/ImageProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 78b53f36a..7d952aa23 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Common.Extensions; @@ -274,7 +275,7 @@ namespace Emby.Drawing string backgroundColor, string foregroundLayer) { - var filename = new StringBuilder(128); + var filename = new StringBuilder(256); filename.Append(originalPath); filename.Append(",quality="); From 01491796a285a621c6aebe4f886ee74413e5fbee Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 11 Apr 2021 12:57:28 +0200 Subject: [PATCH 278/402] Enable Workstation GC mode --- Jellyfin.Server/Jellyfin.Server.csproj | 1 + debian/conf/jellyfin | 5 +++++ fedora/jellyfin.env | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index e7f83aff1..98d990344 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -9,6 +9,7 @@ jellyfin Exe net5.0 + false false true true diff --git a/debian/conf/jellyfin b/debian/conf/jellyfin index 7cbfa88ee..9ebaf2bd8 100644 --- a/debian/conf/jellyfin +++ b/debian/conf/jellyfin @@ -33,6 +33,11 @@ JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg" # [OPTIONAL] run Jellyfin without the web app #JELLYFIN_NOWEBAPP_OPT="--nowebclient" +# [OPTIONAL] run Jellyfin with ASP.NET Server Garbage Collection (uses more RAM and less CPU than Workstation GC) +# 0 = Workstation +# 1 = Server +#COMPlus_gcServer=1 + # # SysV init/Upstart options # diff --git a/fedora/jellyfin.env b/fedora/jellyfin.env index bf64acd3f..56b7a3558 100644 --- a/fedora/jellyfin.env +++ b/fedora/jellyfin.env @@ -35,3 +35,7 @@ JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh" # [OPTIONAL] run Jellyfin without the web app #JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" +# [OPTIONAL] run Jellyfin with ASP.NET Server Garbage Collection (uses more RAM and less CPU than Workstation GC) +# 0 = Workstation +# 1 = Server +#COMPlus_gcServer=1 From 7aa53b060e5e7c4315e203034114b9d585d039da Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 11 Apr 2021 14:34:45 -0400 Subject: [PATCH 279/402] Add label commenter workflow Right now the main purpose is to handle instructions for stable backports. Can be expanded in the future. --- .github/workflows/label-commenter-config.yml | 41 ++++++++++++++++++++ .github/workflows/label-commenter.yml | 22 +++++++++++ 2 files changed, 63 insertions(+) create mode 100644 .github/workflows/label-commenter-config.yml create mode 100644 .github/workflows/label-commenter.yml diff --git a/.github/workflows/label-commenter-config.yml b/.github/workflows/label-commenter-config.yml new file mode 100644 index 000000000..6ee967f55 --- /dev/null +++ b/.github/workflows/label-commenter-config.yml @@ -0,0 +1,41 @@ +comment: + header: Hello @{{ issue.user.login }} + footer: "\ + ---\n\n + > This is an automated comment created by the [peaceiris/actions-label-commenter]. \ + Responding to the bot or mentioning it won't have any effect.\n\n + [peaceiris/actions-label-commenter]: https://github.com/peaceiris/actions-label-commenter + " + +labels: + - name: stable backport + labeled: + pr: + body: | + This pull request has been tagged as a stable backport. It will be cherry-picked into the next stable point release. + + Please observe the following: + + * Any dependent PRs that this PR requires **must** be tagged for stable backporting as well. + + * Any issue(s) this PR fixes or closes **should** target the current stable release or a previous stable release to which a fix has not yet entered the current stable release. + + * This PR **must** be test cherry-picked against the current release branch (`release-X.Y.z` where X and Y are numbers). It must apply cleanly, or a diff of the expected change must be provided. + + To do this, run the following commands from your local copy of the Jellyfin repository: + + 1. `git checkout master` + + 1. `git merge --no-ff ` + + 1. `git log` -> `commit xxxxxxxxx`, grab hash + + 1. `git checkout release-X.Y.z` replacing X and Y with the *current* stable version (e.g. `release-10.7.z`) + + 1. `git cherry-pick -sx -m1 ` + + Ensure the `cherry-pick` applies cleanly. If it does not, fix any merge conflicts *preserving as much of the original code as possible*, and make note of the resulting diff. + + **Do not** push your merges to either branch. Use `git reset --hard HEAD~1` to revert both branches to their original state. + + Reply to this PR with a comment beginning "Cherry-pick test completed." and including the merge-conflict-ixing diff(s) if applicable. diff --git a/.github/workflows/label-commenter.yml b/.github/workflows/label-commenter.yml new file mode 100644 index 000000000..be9216cc1 --- /dev/null +++ b/.github/workflows/label-commenter.yml @@ -0,0 +1,22 @@ +name: Label Commenter + +on: + issues: + types: + - labeled + - unlabeled + pull_request_target: + types: + - labeled + - unlabeled + +jobs: + comment: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + ref: master + + - name: Label Commenter + uses: peaceiris/actions-label-commenter@v1 From f381c536342760a53db85a1ed527091da2d3abfc Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 11 Apr 2021 14:51:45 -0400 Subject: [PATCH 280/402] Fix typo --- .github/workflows/label-commenter-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-commenter-config.yml b/.github/workflows/label-commenter-config.yml index 6ee967f55..c12c5cf85 100644 --- a/.github/workflows/label-commenter-config.yml +++ b/.github/workflows/label-commenter-config.yml @@ -38,4 +38,4 @@ labels: **Do not** push your merges to either branch. Use `git reset --hard HEAD~1` to revert both branches to their original state. - Reply to this PR with a comment beginning "Cherry-pick test completed." and including the merge-conflict-ixing diff(s) if applicable. + Reply to this PR with a comment beginning "Cherry-pick test completed." and including the merge-conflict-fixing diff(s) if applicable. From 4deccd451ea09c3c34d9236f3b7426277b32a0bb Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 11 Apr 2021 16:31:10 -0400 Subject: [PATCH 281/402] Mention testing too --- .github/workflows/label-commenter-config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/label-commenter-config.yml b/.github/workflows/label-commenter-config.yml index c12c5cf85..78b75be43 100644 --- a/.github/workflows/label-commenter-config.yml +++ b/.github/workflows/label-commenter-config.yml @@ -36,6 +36,8 @@ labels: Ensure the `cherry-pick` applies cleanly. If it does not, fix any merge conflicts *preserving as much of the original code as possible*, and make note of the resulting diff. + Test your changes with a build to ensure they are successful. If not, adjust the diff accordingly. + **Do not** push your merges to either branch. Use `git reset --hard HEAD~1` to revert both branches to their original state. Reply to this PR with a comment beginning "Cherry-pick test completed." and including the merge-conflict-fixing diff(s) if applicable. From a4ffc7a813389e8cc2e075d3c9e740a66896ad5c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 12 Apr 2021 00:28:17 +0200 Subject: [PATCH 282/402] Fix random failing of tests Fully initialize the configuration manager at the init stage ``` Failed Jellyfin.Server.Integration.Tests.Controllers.ActivityLogControllerTests.ActivityLog_GetEntries_Ok [2 s] Error Message: MediaBrowser.Common.Extensions.ResourceNotFoundException : Configuration with key metadata not found. Stack Trace: at Emby.Server.Implementations.AppBase.BaseConfigurationManager.<>c__DisplayClass43_0.b__0(String k) in D:\a\1\s\Emby.Server.Implementations\AppBase\BaseConfigurationManager.cs:line 309 at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory) at Emby.Server.Implementations.AppBase.BaseConfigurationManager.GetConfiguration(String key) in D:\a\1\s\Emby.Server.Implementations\AppBase\BaseConfigurationManager.cs:line 300 at MediaBrowser.Common.Configuration.ConfigurationManagerExtensions.GetConfiguration[T](IConfigurationManager manager, String key) in D:\a\1\s\MediaBrowser.Common\Configuration\IConfigurationManager.cs:line 88 at MediaBrowser.Controller.Library.MetadataConfigurationExtensions.GetMetadataConfiguration(IConfigurationManager config) in D:\a\1\s\MediaBrowser.Controller\Library\MetadataConfigurationStore.cs:line 28 at Emby.Server.Implementations.Library.ResolverHelper.SetDateCreated(BaseItem item, IFileSystem fileSystem, FileSystemMetadata info) in D:\a\1\s\Emby.Server.Implementations\Library\ResolverHelper.cs:line 159 at Emby.Server.Implementations.Library.ResolverHelper.EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args) in D:\a\1\s\Emby.Server.Implementations\Library\ResolverHelper.cs:line 153 at Emby.Server.Implementations.Library.ResolverHelper.SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem, ILibraryManager libraryManager) in D:\a\1\s\Emby.Server.Implementations\Library\ResolverHelper.cs:line 81 at Emby.Server.Implementations.Library.LibraryManager.ResolveItem(ItemResolveArgs args, IItemResolver[] resolvers) in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 480 at Emby.Server.Implementations.Library.LibraryManager.ResolvePath(FileSystemMetadata fileInfo, IDirectoryService directoryService, IItemResolver[] resolvers, Folder parent, String collectionType, LibraryOptions libraryOptions) in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 618 at Emby.Server.Implementations.Library.LibraryManager.ResolvePath(FileSystemMetadata fileInfo, Folder parent) in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 536 at Emby.Server.Implementations.Library.LibraryManager.CreateRootFolder() in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 732 at Emby.Server.Implementations.Library.LibraryManager.get_RootFolder() in D:\a\1\s\Emby.Server.Implementations\Library\LibraryManager.cs:line 180 at Emby.Server.Implementations.IO.LibraryMonitor.Start() in D:\a\1\s\Emby.Server.Implementations\IO\LibraryMonitor.cs:line 135 at Emby.Server.Implementations.IO.LibraryMonitorStartup.RunAsync() in D:\a\1\s\Emby.Server.Implementations\IO\LibraryMonitorStartup.cs:line 26 at Emby.Server.Implementations.ApplicationHost.StartEntryPoints(IEnumerable`1 entryPoints, Boolean isBeforeStartup)+MoveNext() in D:\a\1\s\Emby.Server.Implementations\ApplicationHost.cs:line 541 at System.Threading.Tasks.Task.WhenAll(IEnumerable`1 tasks) at Emby.Server.Implementations.ApplicationHost.RunStartupTasksAsync(CancellationToken cancellationToken) in D:\a\1\s\Emby.Server.Implementations\ApplicationHost.cs:line 525 at Jellyfin.Server.Integration.Tests.JellyfinApplicationFactory.CreateServer(IWebHostBuilder builder) in D:\a\1\s\tests\Jellyfin.Server.Integration.Tests\JellyfinApplicationFactory.cs:line 101 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer() at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient() at Jellyfin.Server.Integration.Tests.Controllers.ActivityLogControllerTests.ActivityLog_GetEntries_Ok() in D:\a\1\s\tests\Jellyfin.Server.Integration.Tests\Controllers\ActivityLogControllerTests.cs:line 21 --- End of stack trace from previous location --- ``` --- .../ApplicationHost.cs | 87 ++++++++----------- Jellyfin.Server/Migrations/MigrationRunner.cs | 8 +- Jellyfin.Server/Program.cs | 2 +- .../MetadataConfigurationExtensions.cs | 15 ++++ .../Library/MetadataConfigurationStore.cs | 12 +-- 5 files changed, 59 insertions(+), 65 deletions(-) create mode 100644 MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3846de5fd..0512adf10 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -210,7 +210,7 @@ namespace Emby.Server.Implementations /// Gets or sets the configuration manager. /// /// The configuration manager. - protected IConfigurationManager ConfigurationManager { get; set; } + public ServerConfigurationManager ConfigurationManager { get; set; } /// /// Gets or sets the service provider. @@ -232,12 +232,6 @@ namespace Emby.Server.Implementations /// public string PublishedServerUrl => _startupOptions.PublishedServerUrl ?? _startupConfig[UdpServer.AddressOverrideConfigKey]; - /// - /// Gets the server configuration manager. - /// - /// The server configuration manager. - public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; - /// /// Initializes a new instance of the class. /// @@ -255,43 +249,26 @@ namespace Emby.Server.Implementations IFileSystem fileSystem, IServiceCollection serviceCollection) { - _xmlSerializer = new MyXmlSerializer(); - - ServiceCollection = serviceCollection; - ApplicationPaths = applicationPaths; LoggerFactory = loggerFactory; - _fileSystemManager = fileSystem; - - ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); - // Have to migrate settings here as migration subsystem not yet initialised. - MigrateNetworkConfiguration(); - - // Have to pre-register the NetworkConfigurationFactory, as the configuration sub-system is not yet initialised. - ConfigurationManager.RegisterConfiguration(); - NetManager = new NetworkManager((IServerConfigurationManager)ConfigurationManager, LoggerFactory.CreateLogger()); - - Logger = LoggerFactory.CreateLogger(); - _startupOptions = options; _startupConfig = startupConfig; + _fileSystemManager = fileSystem; + ServiceCollection = serviceCollection; - // Initialize runtime stat collection - if (ServerConfigurationManager.Configuration.EnableMetrics) - { - DotNetRuntimeStatsBuilder.Default().StartCollecting(); - } - + Logger = LoggerFactory.CreateLogger(); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version; ApplicationVersionString = ApplicationVersion.ToString(3); ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString; + _xmlSerializer = new MyXmlSerializer(); + ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); _pluginManager = new PluginManager( LoggerFactory.CreateLogger(), this, - ServerConfigurationManager.Configuration, + ConfigurationManager.Configuration, ApplicationPaths.PluginsPath, ApplicationVersion); } @@ -306,9 +283,9 @@ namespace Emby.Server.Implementations if (!File.Exists(path)) { var networkSettings = new NetworkConfiguration(); - ClassMigrationHelper.CopyProperties(ServerConfigurationManager.Configuration, networkSettings); + ClassMigrationHelper.CopyProperties(ConfigurationManager.Configuration, networkSettings); _xmlSerializer.SerializeToFile(networkSettings, path); - Logger?.LogDebug("Successfully migrated network settings."); + Logger.LogDebug("Successfully migrated network settings."); } } @@ -545,7 +522,21 @@ namespace Emby.Server.Implementations /// public void Init() { - var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration(); + DiscoverTypes(); + + ConfigurationManager.AddParts(GetExports()); + + // Have to migrate settings here as migration subsystem not yet initialised. + MigrateNetworkConfiguration(); + NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger()); + + // Initialize runtime stat collection + if (ConfigurationManager.Configuration.EnableMetrics) + { + DotNetRuntimeStatsBuilder.Default().StartCollecting(); + } + + var networkConfiguration = ConfigurationManager.GetNetworkConfiguration(); HttpPort = networkConfiguration.HttpServerPortNumber; HttpsPort = networkConfiguration.HttpsPortNumber; @@ -563,8 +554,6 @@ namespace Emby.Server.Implementations }; Certificate = GetCertificate(CertificateInfo); - DiscoverTypes(); - RegisterServices(); _pluginManager.RegisterServices(ServiceCollection); @@ -579,7 +568,8 @@ namespace Emby.Server.Implementations ServiceCollection.AddMemoryCache(); - ServiceCollection.AddSingleton(ConfigurationManager); + ServiceCollection.AddSingleton(ConfigurationManager); + ServiceCollection.AddSingleton(ConfigurationManager); ServiceCollection.AddSingleton(this); ServiceCollection.AddSingleton(_pluginManager); ServiceCollection.AddSingleton(ApplicationPaths); @@ -606,8 +596,6 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(this); ServiceCollection.AddSingleton(ApplicationPaths); - ServiceCollection.AddSingleton(ServerConfigurationManager); - ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); @@ -796,7 +784,7 @@ namespace Emby.Server.Implementations { // For now there's no real way to inject these properly BaseItem.Logger = Resolve>(); - BaseItem.ConfigurationManager = ServerConfigurationManager; + BaseItem.ConfigurationManager = ConfigurationManager; BaseItem.LibraryManager = Resolve(); BaseItem.ProviderManager = Resolve(); BaseItem.LocalizationManager = Resolve(); @@ -818,13 +806,12 @@ namespace Emby.Server.Implementations /// private void FindParts() { - if (!ServerConfigurationManager.Configuration.IsPortAuthorized) + if (!ConfigurationManager.Configuration.IsPortAuthorized) { - ServerConfigurationManager.Configuration.IsPortAuthorized = true; + ConfigurationManager.Configuration.IsPortAuthorized = true; ConfigurationManager.SaveConfiguration(); } - ConfigurationManager.AddParts(GetExports()); _pluginManager.CreatePlugins(); _urlPrefixes = GetUrlPrefixes().ToArray(); @@ -928,7 +915,7 @@ namespace Emby.Server.Implementations protected void OnConfigurationUpdated(object sender, EventArgs e) { var requiresRestart = false; - var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration(); + var networkConfiguration = ConfigurationManager.GetNetworkConfiguration(); // Don't do anything if these haven't been set yet if (HttpPort != 0 && HttpsPort != 0) @@ -937,10 +924,10 @@ namespace Emby.Server.Implementations if (networkConfiguration.HttpServerPortNumber != HttpPort || networkConfiguration.HttpsPortNumber != HttpsPort) { - if (ServerConfigurationManager.Configuration.IsPortAuthorized) + if (ConfigurationManager.Configuration.IsPortAuthorized) { - ServerConfigurationManager.Configuration.IsPortAuthorized = false; - ServerConfigurationManager.SaveConfiguration(); + ConfigurationManager.Configuration.IsPortAuthorized = false; + ConfigurationManager.SaveConfiguration(); requiresRestart = true; } @@ -1156,7 +1143,7 @@ namespace Emby.Server.Implementations } /// - public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.GetNetworkConfiguration().EnableHttps; + public bool ListenWithHttps => Certificate != null && ConfigurationManager.GetNetworkConfiguration().EnableHttps; /// public string GetSmartApiUrl(IPAddress ipAddress, int? port = null) @@ -1240,14 +1227,14 @@ namespace Emby.Server.Implementations Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), Host = host, Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), - Path = ServerConfigurationManager.GetNetworkConfiguration().BaseUrl + Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl }.ToString().TrimEnd('/'); } public string FriendlyName => - string.IsNullOrEmpty(ServerConfigurationManager.Configuration.ServerName) + string.IsNullOrEmpty(ConfigurationManager.Configuration.ServerName) ? Environment.MachineName - : ServerConfigurationManager.Configuration.ServerName; + : ConfigurationManager.Configuration.ServerName; /// /// Shuts down. diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 305660ae6..cf938ab8c 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -40,15 +40,15 @@ namespace Jellyfin.Server.Migrations .Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m)) .OfType() .ToArray(); - var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); + var migrationOptions = ((IConfigurationManager)host.ConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); - if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0) + if (!host.ConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0) { // If startup wizard is not finished, this is a fresh install. // Don't run any migrations, just mark all of them as applied. logger.LogInformation("Marking all known migrations as applied because this is a fresh install"); migrationOptions.Applied.AddRange(migrations.Where(m => !m.PerformOnNewInstall).Select(m => (m.Id, m.Name))); - host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); + host.ConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); } var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet(); @@ -77,7 +77,7 @@ namespace Jellyfin.Server.Migrations // Mark the migration as completed logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name); migrationOptions.Applied.Add((migrationRoutine.Id, migrationRoutine.Name)); - host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); + host.ConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name); } } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 4f203b7a9..464e02419 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -173,7 +173,7 @@ namespace Jellyfin.Server // If hosting the web client, validate the client content path if (startupConfig.HostWebClient()) { - string? webContentPath = appHost.ServerConfigurationManager.ApplicationPaths.WebPath; + string? webContentPath = appHost.ConfigurationManager.ApplicationPaths.WebPath; if (!Directory.Exists(webContentPath) || Directory.GetFiles(webContentPath).Length == 0) { throw new InvalidOperationException( diff --git a/MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs b/MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs new file mode 100644 index 000000000..884f9e773 --- /dev/null +++ b/MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs @@ -0,0 +1,15 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; + +namespace MediaBrowser.Controller.Library +{ + public static class MetadataConfigurationExtensions + { + public static MetadataConfiguration GetMetadataConfiguration(this IConfigurationManager config) + { + return config.GetConfiguration("metadata"); + } + } +} diff --git a/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs b/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs index f16304db0..a6be6c0d3 100644 --- a/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs +++ b/MediaBrowser.Controller/Library/MetadataConfigurationStore.cs @@ -14,18 +14,10 @@ namespace MediaBrowser.Controller.Library { new ConfigurationStore { - Key = "metadata", - ConfigurationType = typeof(MetadataConfiguration) + Key = "metadata", + ConfigurationType = typeof(MetadataConfiguration) } }; } } - - public static class MetadataConfigurationExtensions - { - public static MetadataConfiguration GetMetadataConfiguration(this IConfigurationManager config) - { - return config.GetConfiguration("metadata"); - } - } } From 10d358c8da4f5e323c81a5880625737e025d2ba0 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 12 Apr 2021 10:28:24 +0100 Subject: [PATCH 283/402] Update tests/Jellyfin.Networking.Tests/NetworkParseTests.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Odd Stråbø --- tests/Jellyfin.Networking.Tests/NetworkParseTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index b6bbfc5e1..63182974e 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -383,7 +383,8 @@ namespace Jellyfin.Networking.Tests // User on external network, no binding - so result is the 1st external. [InlineData("jellyfin.org", "", false, "eth11")] // Dns failure - should skip the test. - [InlineData("ospoakdposkd.abc", "", false, "eth11")] + // https://en.wikipedia.org/wiki/.test + [InlineData("invalid.domain.test", "", false, "eth11")] // User assumed to be internal, no binding - so result is the 1st internal. [InlineData("", "", false, "eth16")] public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result) From cc59abd54e7be6a58187bfb3d01dbe0ec7ea9a1d Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 12 Apr 2021 07:50:24 -0600 Subject: [PATCH 284/402] Mark cropWhitespace parameter as obsolete (#5751) --- Jellyfin.Api/Controllers/ImageController.cs | 45 +++++++-------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 55c2fe735..36facf75a 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -524,7 +524,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, [FromQuery] string? tag, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] ImageFormat? format, [FromQuery] bool? addPlayedIndicator, [FromQuery] double? percentPlayed, @@ -555,7 +555,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -610,7 +609,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, [FromQuery] string? tag, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] ImageFormat? format, [FromQuery] bool? addPlayedIndicator, [FromQuery] double? percentPlayed, @@ -640,7 +639,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -694,7 +692,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, [FromRoute, Required] string tag, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromRoute, Required] ImageFormat format, [FromQuery] bool? addPlayedIndicator, [FromRoute, Required] double percentPlayed, @@ -725,7 +723,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -782,7 +779,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -810,7 +807,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -867,7 +863,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -895,7 +891,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -953,7 +948,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -980,7 +975,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1037,7 +1031,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1065,7 +1059,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1123,7 +1116,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1150,7 +1143,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1207,7 +1199,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1235,7 +1227,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1293,7 +1284,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1320,7 +1311,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1377,7 +1367,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1405,7 +1395,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1463,7 +1452,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1490,7 +1479,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1547,7 +1535,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1592,7 +1580,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1651,7 +1638,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? quality, [FromQuery] int? fillWidth, [FromQuery] int? fillHeight, - [FromQuery] bool? cropWhitespace, + [FromQuery, ParameterObsolete] bool? cropWhitespace, [FromQuery] bool? addPlayedIndicator, [FromQuery] int? blur, [FromQuery] string? backgroundColor, @@ -1695,7 +1682,6 @@ namespace Jellyfin.Api.Controllers quality, fillWidth, fillHeight, - cropWhitespace, addPlayedIndicator, blur, backgroundColor, @@ -1781,7 +1767,6 @@ namespace Jellyfin.Api.Controllers int? quality, int? fillWidth, int? fillHeight, - bool? cropWhitespace, // TODO: Remove bool? addPlayedIndicator, int? blur, string? backgroundColor, @@ -1823,8 +1808,6 @@ namespace Jellyfin.Api.Controllers } } - cropWhitespace ??= imageType == ImageType.Logo || imageType == ImageType.Art; - var outputFormats = GetOutputFormats(format); TimeSpan? cacheDuration = null; From dc4714fe4098eed42114adc3455f4a2982cc18d6 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 12 Apr 2021 19:54:32 +0200 Subject: [PATCH 285/402] fix webp compatibility testing (#5787) --- Jellyfin.Api/Controllers/ImageController.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 36facf75a..8f7500ac6 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1859,17 +1859,15 @@ namespace Jellyfin.Api.Controllers private ImageFormat[] GetClientSupportedFormats() { - var acceptTypes = Request.Headers[HeaderNames.Accept]; - var supportedFormats = new List(); - if (acceptTypes.Count > 0) + var supportedFormats = Request.Headers.GetCommaSeparatedValues(HeaderNames.Accept); + for (var i = 0; i < supportedFormats.Length; i++) { - foreach (var type in acceptTypes) + // Remove charsets etc. (anything after semi-colon) + var type = supportedFormats[i]; + int index = type.IndexOf(';', StringComparison.Ordinal); + if (index != -1) { - int index = type.IndexOf(';', StringComparison.Ordinal); - if (index != -1) - { - supportedFormats.Add(type.Substring(0, index)); - } + supportedFormats[i] = type.Substring(0, index); } } From 8045a488ceccc5c85195be3ef694a418c1c326aa Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 12 Apr 2021 22:01:35 +0200 Subject: [PATCH 286/402] Fix possible ArgumentNullException ``` Error Message: System.ArgumentNullException : Value cannot be null. (Parameter 'source') Stack Trace: at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector) at Emby.Server.Implementations.Library.LibraryManager.ResolveItem(ItemResolveArgs args, IItemResolver[] resolvers) in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 475 at Emby.Server.Implementations.Library.LibraryManager.ResolvePath(FileSystemMetadata fileInfo, IDirectoryService directoryService, IItemResolver[] resolvers, Folder parent, String collectionType, LibraryOptions libraryOptions) in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 618 at Emby.Server.Implementations.Library.LibraryManager.ResolvePath(FileSystemMetadata fileInfo, Folder parent) in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 536 at Emby.Server.Implementations.Library.LibraryManager.GetUserRootFolder() in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 812 at Emby.Server.Implementations.Library.LibraryManager.GetCollectionFolders(BaseItem item) in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 2080 at Emby.Server.Implementations.Library.LibraryManager.GetLibraryOptions(BaseItem item) in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 2116 at MediaBrowser.Providers.Manager.ProviderManager.SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable`1 savers) in /home/vsts/work/1/s/MediaBrowser.Providers/Manager/ProviderManager.cs:line 672 at MediaBrowser.Providers.Manager.ProviderManager.SaveMetadata(BaseItem item, ItemUpdateType updateType) in /home/vsts/work/1/s/MediaBrowser.Providers/Manager/ProviderManager.cs:line 655 at Emby.Server.Implementations.Library.LibraryManager.RunMetadataSavers(BaseItem item, ItemUpdateType updateReason) in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 2017 at Emby.Server.Implementations.Library.LibraryManager.UpdateItemsAsync(IReadOnlyList`1 items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 1975 at Emby.Server.Implementations.Library.LibraryManager.CreateRootFolder() in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 775 at Emby.Server.Implementations.Library.LibraryManager.get_RootFolder() in /home/vsts/work/1/s/Emby.Server.Implementations/Library/LibraryManager.cs:line 180 at Emby.Server.Implementations.IO.LibraryMonitor.Start() in /home/vsts/work/1/s/Emby.Server.Implementations/IO/LibraryMonitor.cs:line 135 at Emby.Server.Implementations.IO.LibraryMonitorStartup.RunAsync() in /home/vsts/work/1/s/Emby.Server.Implementations/IO/LibraryMonitorStartup.cs:line 26 at Emby.Server.Implementations.ApplicationHost.StartEntryPoints(IEnumerable`1 entryPoints, Boolean isBeforeStartup)+MoveNext() in /home/vsts/work/1/s/Emby.Server.Implementations/ApplicationHost.cs:line 518 at System.Threading.Tasks.Task.WhenAll(IEnumerable`1 tasks) at Emby.Server.Implementations.ApplicationHost.RunStartupTasksAsync(CancellationToken cancellationToken) in /home/vsts/work/1/s/Emby.Server.Implementations/ApplicationHost.cs:line 502 at Jellyfin.Server.Integration.Tests.JellyfinApplicationFactory.CreateServer(IWebHostBuilder builder) in /home/vsts/work/1/s/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs:line 101 at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer() at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient() at Jellyfin.Server.Integration.Tests.Controllers.ActivityLogControllerTests.ActivityLog_GetEntries_Ok() in /home/vsts/work/1/s/tests/Jellyfin.Server.Integration.Tests/Controllers/ActivityLogControllerTests.cs:line 21 --- End of stack trace from previous location --- ``` --- Emby.Server.Implementations/Library/LibraryManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 7f29e3e99..6a9f4174d 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -196,13 +196,13 @@ namespace Emby.Server.Implementations.Library /// Gets or sets the postscan tasks. /// /// The postscan tasks. - private ILibraryPostScanTask[] PostscanTasks { get; set; } + private ILibraryPostScanTask[] PostscanTasks { get; set; } = Array.Empty(); /// /// Gets or sets the intro providers. /// /// The intro providers. - private IIntroProvider[] IntroProviders { get; set; } + private IIntroProvider[] IntroProviders { get; set; } = Array.Empty(); /// /// Gets or sets the list of entity resolution ignore rules. @@ -214,15 +214,15 @@ namespace Emby.Server.Implementations.Library /// Gets or sets the list of currently registered entity resolvers. /// /// The entity resolvers enumerable. - private IItemResolver[] EntityResolvers { get; set; } + private IItemResolver[] EntityResolvers { get; set; } = Array.Empty(); - private IMultiItemResolver[] MultiItemResolvers { get; set; } + private IMultiItemResolver[] MultiItemResolvers { get; set; } = Array.Empty(); /// /// Gets or sets the comparers. /// /// The comparers. - private IBaseItemComparer[] Comparers { get; set; } + private IBaseItemComparer[] Comparers { get; set; } = Array.Empty(); public bool IsScanRunning { get; private set; } From fe54e9ae9de932d2130f15d90df40da9dbc048fe Mon Sep 17 00:00:00 2001 From: Rich Lander Date: Mon, 12 Apr 2021 17:16:26 -0700 Subject: [PATCH 287/402] Remove coupling to distro - The substring `-buster-slim` isn't useful/correct. - .NET 6 (from my team) is based exclusively on bullseye. - If you update just `DOTNET_VERSION` to `6.0`, `docker build` will fail. - If the goal is to force using `buster` throughout the Dockerfile, then making `DOTNET_VERSION` configurable isn't correct. - Context: https://github.com/dotnet/dotnet-docker/blob/main/src/runtime-deps/6.0/bullseye-slim/amd64/Dockerfile#L1 Just saw this as a drive by look at your code. Is intended as helpful context. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cafee737a..ebe5eb00c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine- && npm ci --no-audit \ && mv dist /dist -FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim as builder +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder WORKDIR /repo COPY . . ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 From 63355af5186756df0538fdf56e99c045864f9ff1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Apr 2021 13:09:10 +0000 Subject: [PATCH 288/402] Bump BlurHashSharp.SkiaSharp from 1.1.1 to 1.2.0 Bumps [BlurHashSharp.SkiaSharp](https://github.com/Bond-009/BlurHashSharp) from 1.1.1 to 1.2.0. - [Release notes](https://github.com/Bond-009/BlurHashSharp/releases) - [Commits](https://github.com/Bond-009/BlurHashSharp/commits/v1.2.0) Signed-off-by: dependabot[bot] --- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 5f9c4d679..ee43c2159 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -21,7 +21,7 @@ - + From f1c4b541c1d3ef960921a9480022467a940adae0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Apr 2021 13:09:28 +0000 Subject: [PATCH 289/402] Bump TMDbLib from 1.7.3-alpha to 1.8.1 Bumps [TMDbLib](https://github.com/LordMike/TMDbLib) from 1.7.3-alpha to 1.8.1. - [Release notes](https://github.com/LordMike/TMDbLib/releases) - [Commits](https://github.com/LordMike/TMDbLib/compare/v1.7.3-alpha...v1.8.1) Signed-off-by: dependabot[bot] --- 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 152ea664a..de7860b2e 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -22,7 +22,7 @@ - + From 723b6abcb392b1a65e46db91aa35f7e13de0c2a8 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 13 Apr 2021 15:37:11 +0200 Subject: [PATCH 290/402] Optimize the way items are grouped into collections --- .../Collections/CollectionManager.cs | 66 ++++++++++++------- MediaBrowser.Controller/Entities/Folder.cs | 4 +- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index db532ce5b..6dbbd5530 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -124,7 +124,7 @@ namespace Emby.Server.Implementations.Collections private IEnumerable GetCollections(User user) { - var folder = GetCollectionsFolder(false).Result; + var folder = GetCollectionsFolder(false).GetAwaiter().GetResult(); return folder == null ? Enumerable.Empty() @@ -319,11 +319,11 @@ namespace Emby.Server.Implementations.Collections { var results = new Dictionary(); - var allBoxsets = GetCollections(user).ToList(); + var allBoxSets = GetCollections(user).ToList(); foreach (var item in items) { - if (!(item is ISupportsBoxSetGrouping)) + if (item is not ISupportsBoxSetGrouping) { results[item.Id] = item; } @@ -331,33 +331,49 @@ namespace Emby.Server.Implementations.Collections { var itemId = item.Id; - var currentBoxSets = allBoxsets - .Where(i => i.ContainsLinkedChildByItemId(itemId)) - .ToList(); - - if (currentBoxSets.Count > 0) + var itemIsInBoxSet = false; + foreach (var boxSet in allBoxSets) { - foreach (var boxset in currentBoxSets) + if (!boxSet.ContainsLinkedChildByItemId(itemId)) { - results[boxset.Id] = boxset; + continue; + } + + itemIsInBoxSet = true; + + if (results.ContainsKey(boxSet.Id)) + { + continue; + } + + results[boxSet.Id] = boxSet; + } + + // skip any item that is in a box set + if (itemIsInBoxSet) + { + continue; + } + + var alreadyInResults = false; + // this is kind of a performance hack because only Video has alternate versions that should be in a box set? + if (item is Video video) + { + foreach (var childId in video.GetLocalAlternateVersionIds()) + { + if (!results.ContainsKey(childId)) + { + continue; + } + + alreadyInResults = true; + break; } } - else - { - var alreadyInResults = false; - foreach (var child in item.GetMediaSources(true)) - { - if (Guid.TryParse(child.Id, out var id) && results.ContainsKey(id)) - { - alreadyInResults = true; - break; - } - } - if (!alreadyInResults) - { - results[item.Id] = item; - } + if (!alreadyInResults) + { + results[item.Id] = item; } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index cac5026f7..485733abb 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1434,9 +1434,9 @@ namespace MediaBrowser.Controller.Entities var linkedChildren = LinkedChildren; foreach (var i in linkedChildren) { - if (i.ItemId.HasValue && i.ItemId.Value == itemId) + if (i.ItemId.HasValue) { - return true; + return i.ItemId.Value == itemId; } var child = GetLinkedChild(i); From 2b948aead910f12ce89da9099d6cefce2810cf8a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 13 Apr 2021 19:53:09 +0200 Subject: [PATCH 291/402] Fix possible ArgumentNullException ``` System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at MediaBrowser.Providers.Manager.ProviderManager.GetImages(BaseItem item, IRemoteImageProvider provider, IReadOnlyCollection`1 preferredLanguages, CancellationToken cancellationToken, Nullable`1 type) in /home/bond/dev/jellyfin/MediaBrowser.Providers/Manager/ProviderManager.cs:line 280 ``` --- .../Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index a96fc8ed6..326c116b3 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -54,7 +54,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (string.IsNullOrEmpty(tmdbId)) { - return null; + return Enumerable.Empty(); } var language = item.GetPreferredMetadataLanguage(); From 27202ae8aa2bd92d69a92328e8e762acddb64add Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Apr 2021 17:11:34 +0000 Subject: [PATCH 292/402] Bump AutoFixture from 4.15.0 to 4.16.0 Bumps [AutoFixture](https://github.com/AutoFixture/AutoFixture) from 4.15.0 to 4.16.0. - [Release notes](https://github.com/AutoFixture/AutoFixture/releases) - [Commits](https://github.com/AutoFixture/AutoFixture/compare/v4.15.0...v4.16.0) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index f288561b7..1306783c9 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index ee59dad5a..38de05ad0 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -22,7 +22,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 8d4d9e3d2..d7e56ff13 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 4a5cf1fee..51d6333b7 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,7 +10,7 @@ - + From d459f625d53d3469f2334c1978ed2779fb0a433a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Apr 2021 17:11:49 +0000 Subject: [PATCH 293/402] Bump Newtonsoft.Json from 12.0.3 to 13.0.1 Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.3 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.3...13.0.1) Signed-off-by: dependabot[bot] --- 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 de7860b2e..cdb07a15d 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -19,7 +19,7 @@ - + From 3d0a42da9e52f53acd797e8f93f6f76fa0ef3f06 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 13 Apr 2021 20:09:50 +0200 Subject: [PATCH 294/402] Remove throttle in refresh code --- .../Manager/ProviderManager.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index b4b0b826f..82ee59da9 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -1074,17 +1074,16 @@ namespace MediaBrowser.Providers.Manager try { var item = libraryManager.GetItemById(refreshItem.Item1); - if (item != null) + if (item == null) { - // Try to throttle this a little bit. - await Task.Delay(100, cancellationToken).ConfigureAwait(false); - - var task = item is MusicArtist artist - ? RefreshArtist(artist, refreshItem.Item2, cancellationToken) - : RefreshItem(item, refreshItem.Item2, cancellationToken); - - await task.ConfigureAwait(false); + continue; } + + var task = item is MusicArtist artist + ? RefreshArtist(artist, refreshItem.Item2, cancellationToken) + : RefreshItem(item, refreshItem.Item2, cancellationToken); + + await task.ConfigureAwait(false); } catch (OperationCanceledException) { From d44b2e2ee5ecb06016b8b88eb03fc6dc28a04ee9 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 13 Apr 2021 20:12:50 +0200 Subject: [PATCH 295/402] fixes --- .../Collections/CollectionManager.cs | 9 ++------- MediaBrowser.Controller/Entities/Folder.cs | 7 ++++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 6dbbd5530..81758d9a7 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -341,12 +341,7 @@ namespace Emby.Server.Implementations.Collections itemIsInBoxSet = true; - if (results.ContainsKey(boxSet.Id)) - { - continue; - } - - results[boxSet.Id] = boxSet; + results.TryAdd(boxSet.Id, boxSet); } // skip any item that is in a box set @@ -373,7 +368,7 @@ namespace Emby.Server.Implementations.Collections if (!alreadyInResults) { - results[item.Id] = item; + results[itemId] = item; } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 485733abb..bd1fbb473 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1436,7 +1436,12 @@ namespace MediaBrowser.Controller.Entities { if (i.ItemId.HasValue) { - return i.ItemId.Value == itemId; + if (i.ItemId.Value == itemId) + { + return true; + } + + continue; } var child = GetLinkedChild(i); From 0b774eac12a882fbdc1ed402f36aaf6e4341e2c2 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 14 Apr 2021 10:26:05 +0100 Subject: [PATCH 296/402] Enables the ability to bind to loopback address. (#5773) --- Jellyfin.Networking/Manager/NetworkManager.cs | 70 +++++++++++-------- MediaBrowser.Common/Net/NetworkExtensions.cs | 2 +- .../NetworkParseTests.cs | 10 +-- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 2f5a5b6e3..73e8b2cd7 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -647,6 +647,16 @@ namespace Jellyfin.Networking.Manager _interfaceAddresses.AddItem(address, false); _interfaceNames[parts[2]] = Math.Abs(index); } + + if (IsIP4Enabled) + { + _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback); + } + + if (IsIP6Enabled) + { + _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback); + } } InitialiseLAN(config); @@ -978,8 +988,8 @@ namespace Jellyfin.Networking.Manager } // Read and parse bind addresses and exclusions, removing ones that don't exist. - _bindAddresses = CreateIPCollection(lanAddresses).Union(_interfaceAddresses); - _bindExclusions = CreateIPCollection(lanAddresses, true).Union(_interfaceAddresses); + _bindAddresses = CreateIPCollection(lanAddresses).ThatAreContainedInNetworks(_interfaceAddresses); + _bindExclusions = CreateIPCollection(lanAddresses, true).ThatAreContainedInNetworks(_interfaceAddresses); _logger.LogInformation("Using bind addresses: {0}", _bindAddresses.AsString()); _logger.LogInformation("Using bind exclusions: {0}", _bindExclusions.AsString()); } @@ -1153,36 +1163,40 @@ namespace Jellyfin.Networking.Manager } #pragma warning restore CA1031 // Do not catch general exception types } - - _logger.LogDebug("Discovered {0} interfaces.", _interfaceAddresses.Count); - _logger.LogDebug("Interfaces addresses : {0}", _interfaceAddresses.AsString()); - - // If for some reason we don't have an interface info, resolve our DNS name. - if (_interfaceAddresses.Count == 0) - { - _logger.LogError("No interfaces information available. Resolving DNS name."); - IPHost host = new IPHost(Dns.GetHostName()); - foreach (var a in host.GetAddresses()) - { - _interfaceAddresses.AddItem(a); - } - - if (_interfaceAddresses.Count == 0) - { - _logger.LogWarning("No interfaces information available. Using loopback."); - // Last ditch attempt - use loopback address. - _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback, false); - if (IsIP6Enabled) - { - _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback, false); - } - } - } } - catch (NetworkInformationException ex) + catch (Exception ex) { _logger.LogError(ex, "Error in InitialiseInterfaces."); } + + // If for some reason we don't have an interface info, resolve our DNS name. + if (_interfaceAddresses.Count == 0) + { + _logger.LogError("No interfaces information available. Resolving DNS name."); + IPHost host = new IPHost(Dns.GetHostName()); + foreach (var a in host.GetAddresses()) + { + _interfaceAddresses.AddItem(a); + } + + if (_interfaceAddresses.Count == 0) + { + _logger.LogWarning("No interfaces information available. Using loopback."); + } + } + + if (IsIP4Enabled) + { + _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback); + } + + if (IsIP6Enabled) + { + _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback); + } + + _logger.LogDebug("Discovered {0} interfaces.", _interfaceAddresses.Count); + _logger.LogDebug("Interfaces addresses : {0}", _interfaceAddresses.AsString()); } } diff --git a/MediaBrowser.Common/Net/NetworkExtensions.cs b/MediaBrowser.Common/Net/NetworkExtensions.cs index 93cfb4817..264bfacb4 100644 --- a/MediaBrowser.Common/Net/NetworkExtensions.cs +++ b/MediaBrowser.Common/Net/NetworkExtensions.cs @@ -232,7 +232,7 @@ namespace MediaBrowser.Common.Net /// The . /// Collection to compare with. /// A collection containing all the matches. - public static Collection Union(this Collection source, Collection target) + public static Collection ThatAreContainedInNetworks(this Collection source, Collection target) { if (source.Count == 0) { diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 2d3356998..9b0da2b3c 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -34,10 +34,10 @@ namespace Jellyfin.Networking.Tests [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.0/24", "[192.168.1.208/24,200.200.200.200/24]")] // eth16 only [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")] - // All interfaces excluded. - [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")] + // All interfaces excluded. (including loopbacks) + [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[127.0.0.1/8,::1/128]")] // vEthernet1 and vEthernet212 should be excluded. - [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")] + [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24,127.0.0.1/8,::1/128]")] // Overlapping interface, [InlineData("192.168.1.110/24,-20,br0|192.168.1.10/24,-16,br0|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.110/24,192.168.1.10/24]")] public void IgnoreVirtualInterfaces(string interfaces, string lan, string value) @@ -241,7 +241,7 @@ namespace Jellyfin.Networking.Tests Collection nc1 = nm.CreateIPCollection(settings.Split(','), false); Collection nc2 = nm.CreateIPCollection(compare.Split(','), false); - Assert.Equal(nc1.Union(nc2).AsString(), result); + Assert.Equal(nc1.ThatAreContainedInNetworks(nc2).AsString(), result); } [Theory] @@ -350,7 +350,7 @@ namespace Jellyfin.Networking.Tests // Test included, IP6. Collection ncSource = nm.CreateIPCollection(source.Split(',')); Collection ncDest = nm.CreateIPCollection(dest.Split(',')); - Collection ncResult = ncSource.Union(ncDest); + Collection ncResult = ncSource.ThatAreContainedInNetworks(ncDest); Collection resultCollection = nm.CreateIPCollection(result.Split(',')); Assert.True(ncResult.Compare(resultCollection)); } From 55700e0d1cc83557f5d5da3d5250bb3702215a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Tue, 9 Mar 2021 21:13:27 +0100 Subject: [PATCH 297/402] Add workflows for rebase and project automation --- .github/dependabot.yml | 8 +++- .github/workflows/automation.yml | 66 +++++++++++++++++++++++++++ .github/workflows/merge-conflicts.yml | 17 +++++++ .github/workflows/rebase.yml | 27 +++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/automation.yml create mode 100644 .github/workflows/merge-conflicts.yml create mode 100644 .github/workflows/rebase.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0874cae2e..70bcd4973 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,4 +6,10 @@ updates: interval: weekly time: '12:00' open-pull-requests-limit: 10 - + +- package-ecosystem: github-actions + directory: '/' + schedule: + interval: weekly + time: '12:00' + open-pull-requests-limit: 10 diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml new file mode 100644 index 000000000..db34693cc --- /dev/null +++ b/.github/workflows/automation.yml @@ -0,0 +1,66 @@ +name: Automation + +on: + pull_request: + issues: + issue_comment: + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Does PR has the stable backport label? + uses: Dreamcodeio/does-pr-has-label@v1.2 + id: checkLabel + with: + label: stable backport + + - name: Remove from 'Current Release' project + uses: alex-page/github-project-automation-plus@v0.5.1 + if: (github.event.pull_request || github.event.issue.pull_request) && !steps.checkLabel.outputs.hasLabel + continue-on-error: true + with: + project: Current Release + action: delete + repo-token: ${{ secrets.GH_TOKEN }} + + - name: Add to 'Release Next' project + uses: alex-page/github-project-automation-plus@v0.5.1 + if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened' + continue-on-error: true + with: + project: Release Next + column: In progress + repo-token: ${{ secrets.GH_TOKEN }} + + - name: Add to 'Current Release' project + uses: alex-page/github-project-automation-plus@v0.5.1 + if: (github.event.pull_request || github.event.issue.pull_request) && steps.checkLabel.outputs.hasLabel + continue-on-error: true + with: + project: Current Release + column: In progress + repo-token: ${{ secrets.GH_TOKEN }} + + - name: Check number of comments from the team member + if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' + id: member_comments + run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)" + + - name: Move issue to needs triage + uses: alex-page/github-project-automation-plus@v0.5.1 + if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1 + continue-on-error: true + with: + project: Issue Triage for Main Repo + column: Needs triage + repo-token: ${{ secrets.GH_TOKEN }} + + - name: Add issue to triage project + uses: alex-page/github-project-automation-plus@v0.5.1 + if: github.event.issue.pull_request == '' && github.event.action == 'opened' + continue-on-error: true + with: + project: Issue Triage for Main Repo + column: Pending response + repo-token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/merge-conflicts.yml b/.github/workflows/merge-conflicts.yml new file mode 100644 index 000000000..3740ce7a8 --- /dev/null +++ b/.github/workflows/merge-conflicts.yml @@ -0,0 +1,17 @@ +name: 'Merge Conflicts' + +on: + push: + branches: + - master + pull_request_target: + types: + - synchronize +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: eps1lon/actions-label-merge-conflict@v2.0.0 + with: + dirtyLabel: 'merge conflict' + repoToken: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml new file mode 100644 index 000000000..3172ec0d9 --- /dev/null +++ b/.github/workflows/rebase.yml @@ -0,0 +1,27 @@ +name: Automatic Rebase +on: + issue_comment: + +jobs: + rebase: + name: Rebase + if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '@jellyfin-bot rebase') && github.event.comment.author_association == 'MEMBER' + runs-on: ubuntu-latest + steps: + - name: Notify as seen + uses: peter-evans/create-or-update-comment@v1.4.5 + with: + token: ${{ secrets.GH_TOKEN }} + comment-id: ${{ github.event.comment.id }} + reactions: '+1' + + - name: Checkout the latest code + uses: actions/checkout@v2 + with: + token: ${{ secrets.GH_TOKEN }} + fetch-depth: 0 + + - name: Automatic Rebase + uses: cirrus-actions/rebase@1.4 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} From 1d4c8dcde3669b21cec2324ed535cfdd8e5737a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Apr 2021 12:51:54 +0000 Subject: [PATCH 298/402] Bump eps1lon/actions-label-merge-conflict from v2.0.0 to v2.0.1 Bumps [eps1lon/actions-label-merge-conflict](https://github.com/eps1lon/actions-label-merge-conflict) from v2.0.0 to v2.0.1. - [Release notes](https://github.com/eps1lon/actions-label-merge-conflict/releases) - [Changelog](https://github.com/eps1lon/actions-label-merge-conflict/blob/main/CHANGELOG.md) - [Commits](https://github.com/eps1lon/actions-label-merge-conflict/compare/v2.0.0...b8bf8341285ec9a4567d4318ba474fee998a6919) Signed-off-by: dependabot[bot] --- .github/workflows/merge-conflicts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge-conflicts.yml b/.github/workflows/merge-conflicts.yml index 3740ce7a8..ce808617a 100644 --- a/.github/workflows/merge-conflicts.yml +++ b/.github/workflows/merge-conflicts.yml @@ -11,7 +11,7 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: eps1lon/actions-label-merge-conflict@v2.0.0 + - uses: eps1lon/actions-label-merge-conflict@v2.0.1 with: dirtyLabel: 'merge conflict' repoToken: ${{ secrets.GH_TOKEN }} From b63f615fd4f92f40394d04c9ae764b35aadc000d Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 15 Apr 2021 07:39:59 -0600 Subject: [PATCH 299/402] Enable nullability for ServerDiscoveryInfo (#5804) --- Emby.Server.Implementations/Udp/UdpServer.cs | 7 +--- .../ApiClient/ServerDiscoveryInfo.cs | 41 ++++++++++++------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index d01184e0b..db5265e79 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -53,12 +53,7 @@ namespace Emby.Server.Implementations.Udp if (!string.IsNullOrEmpty(localUrl)) { - var response = new ServerDiscoveryInfo - { - Address = localUrl, - Id = _appHost.SystemId, - Name = _appHost.FriendlyName - }; + var response = new ServerDiscoveryInfo(localUrl, _appHost.SystemId, _appHost.FriendlyName); try { diff --git a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs index fcc90a1f7..f9f474586 100644 --- a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs +++ b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs @@ -1,32 +1,43 @@ -#nullable disable -#pragma warning disable CS1591 - namespace MediaBrowser.Model.ApiClient { + /// + /// The server discovery info model. + /// public class ServerDiscoveryInfo { /// - /// Gets or sets the address. + /// Initializes a new instance of the class. /// - /// The address. - public string Address { get; set; } + /// The server address. + /// The server id. + /// The server name. + /// The endpoint address. + public ServerDiscoveryInfo(string address, string id, string name, string? endpointAddress = null) + { + Address = address; + Id = id; + Name = name; + EndpointAddress = endpointAddress; + } /// - /// Gets or sets the server identifier. + /// Gets the address. /// - /// The server identifier. - public string Id { get; set; } + public string Address { get; } /// - /// Gets or sets the name. + /// Gets the server identifier. /// - /// The name. - public string Name { get; set; } + public string Id { get; } /// - /// Gets or sets the endpoint address. + /// Gets the name. /// - /// The endpoint address. - public string EndpointAddress { get; set; } + public string Name { get; } + + /// + /// Gets the endpoint address. + /// + public string? EndpointAddress { get; } } } From 3199d1c902f62b1a2697a8361bf810c3766b7f0b Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 15 Apr 2021 18:36:47 +0100 Subject: [PATCH 300/402] Fix: PlayTo using external ip not internal --- Emby.Dlna/PlayTo/PlayToManager.cs | 2 +- Emby.Dlna/Ssdp/DeviceDiscovery.cs | 2 +- MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs | 2 ++ RSSDP/DeviceAvailableEventArgs.cs | 2 +- RSSDP/SsdpDeviceLocator.cs | 26 +++++++++++------------ 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index a6793a708..e9cab17f0 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -183,7 +183,7 @@ namespace Emby.Dlna.PlayTo _sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName); - string serverAddress = _appHost.GetSmartApiUrl(info.LocalIpAddress); + string serverAddress = _appHost.GetSmartApiUrl(info.RemoteIpAddress); controller = new PlayToController( sessionInfo, diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index 8c7d961f3..70223cbdb 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs @@ -104,7 +104,7 @@ namespace Emby.Dlna.Ssdp { Location = e.DiscoveredDevice.DescriptionLocation, Headers = headers, - LocalIpAddress = e.LocalIpAddress + RemoteIpAddress = e.RemoteIpAddress }); DeviceDiscoveredInternal?.Invoke(this, args); diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs index d71013f01..987a3a908 100644 --- a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs +++ b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs @@ -16,5 +16,7 @@ namespace MediaBrowser.Model.Dlna public IPAddress LocalIpAddress { get; set; } public int LocalPort { get; set; } + + public IPAddress RemoteIpAddress { get; set; } } } diff --git a/RSSDP/DeviceAvailableEventArgs.cs b/RSSDP/DeviceAvailableEventArgs.cs index b7d22a7df..04b14c4dc 100644 --- a/RSSDP/DeviceAvailableEventArgs.cs +++ b/RSSDP/DeviceAvailableEventArgs.cs @@ -8,7 +8,7 @@ namespace Rssdp /// public sealed class DeviceAvailableEventArgs : EventArgs { - public IPAddress LocalIpAddress { get; set; } + public IPAddress RemoteIpAddress { get; set; } private readonly DiscoveredSsdpDevice _DiscoveredDevice; diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index bfad6de97..eb99788f0 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -208,7 +208,7 @@ namespace Rssdp.Infrastructure /// Raises the event. /// /// - protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress localIpAddress) + protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress IpAddress) { if (this.IsDisposed) { @@ -220,7 +220,7 @@ namespace Rssdp.Infrastructure { handlers(this, new DeviceAvailableEventArgs(device, isNewDevice) { - LocalIpAddress = localIpAddress + RemoteIpAddress = IpAddress }); } } @@ -286,7 +286,7 @@ namespace Rssdp.Infrastructure } } - private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IPAddress localIpAddress) + private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IPAddress IpAddress) { bool isNewDevice = false; lock (_Devices) @@ -304,17 +304,17 @@ namespace Rssdp.Infrastructure } } - DeviceFound(device, isNewDevice, localIpAddress); + DeviceFound(device, isNewDevice, IpAddress); } - private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress localIpAddress) + private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IPAddress IpAddress) { if (!NotificationTypeMatchesFilter(device)) { return; } - OnDeviceAvailable(device, isNewDevice, localIpAddress); + OnDeviceAvailable(device, isNewDevice, IpAddress); } private bool NotificationTypeMatchesFilter(DiscoveredSsdpDevice device) @@ -347,7 +347,7 @@ namespace Rssdp.Infrastructure return _CommunicationsServer.SendMulticastMessage(message, null, cancellationToken); } - private void ProcessSearchResponseMessage(HttpResponseMessage message, IPAddress localIpAddress) + private void ProcessSearchResponseMessage(HttpResponseMessage message, IPAddress IpAddress) { if (!message.IsSuccessStatusCode) { @@ -367,11 +367,11 @@ namespace Rssdp.Infrastructure ResponseHeaders = message.Headers }; - AddOrUpdateDiscoveredDevice(device, localIpAddress); + AddOrUpdateDiscoveredDevice(device, IpAddress); } } - private void ProcessNotificationMessage(HttpRequestMessage message, IPAddress localIpAddress) + private void ProcessNotificationMessage(HttpRequestMessage message, IPAddress IpAddress) { if (String.Compare(message.Method.Method, "Notify", StringComparison.OrdinalIgnoreCase) != 0) { @@ -381,7 +381,7 @@ namespace Rssdp.Infrastructure var notificationType = GetFirstHeaderStringValue("NTS", message); if (String.Compare(notificationType, SsdpConstants.SsdpKeepAliveNotification, StringComparison.OrdinalIgnoreCase) == 0) { - ProcessAliveNotification(message, localIpAddress); + ProcessAliveNotification(message, IpAddress); } else if (String.Compare(notificationType, SsdpConstants.SsdpByeByeNotification, StringComparison.OrdinalIgnoreCase) == 0) { @@ -389,7 +389,7 @@ namespace Rssdp.Infrastructure } } - private void ProcessAliveNotification(HttpRequestMessage message, IPAddress localIpAddress) + private void ProcessAliveNotification(HttpRequestMessage message, IPAddress IpAddress) { var location = GetFirstHeaderUriValue("Location", message); if (location != null) @@ -404,7 +404,7 @@ namespace Rssdp.Infrastructure ResponseHeaders = message.Headers }; - AddOrUpdateDiscoveredDevice(device, localIpAddress); + AddOrUpdateDiscoveredDevice(device, IpAddress); } } @@ -630,7 +630,7 @@ namespace Rssdp.Infrastructure private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e) { - ProcessNotificationMessage(e.Message, e.LocalIpAddress); + ProcessNotificationMessage(e.Message, e.ReceivedFrom.Address); } } } From bb6fddde9ac48905b876717806754a052bf0ad24 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 17 Apr 2021 11:19:09 +0100 Subject: [PATCH 301/402] Group Methods --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 2 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 4 ++-- Jellyfin.Networking/Manager/NetworkManager.cs | 2 +- .../Migrations/Routines/CreateUserLoggingConfigFile.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 2 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 932f721ab..2d1a559f1 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.Playlists // Create a list of the new linked children to add to the playlist var childrenToAdd = newItems - .Select(i => LinkedChild.Create(i)) + .Select(LinkedChild.Create) .ToList(); // Log duplicates that have been ignored, if any diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index e2306aa27..404cb3a46 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -121,14 +121,14 @@ namespace Jellyfin.Api.Helpers if (!string.IsNullOrWhiteSpace(streamingRequest.AudioCodec)) { state.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',', StringSplitOptions.RemoveEmptyEntries); - state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i)) + state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(mediaEncoder.CanEncodeToAudioCodec) ?? state.SupportedAudioCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(streamingRequest.SubtitleCodec)) { state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',', StringSplitOptions.RemoveEmptyEntries); - state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i)) + state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(mediaEncoder.CanEncodeToSubtitleCodec) ?? state.SupportedSubtitleCodecs.FirstOrDefault(); } diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 73e8b2cd7..4078fd126 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -1067,7 +1067,7 @@ namespace Jellyfin.Networking.Manager } // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet. - _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i))); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(IsInLocalNetwork)); } _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString()); diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 6821630db..ee4f8b0ba 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -75,7 +75,7 @@ namespace Jellyfin.Server.Migrations.Routines { var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath)); return _defaultConfigHistory - .Select(historicalConfigText => JToken.Parse(historicalConfigText)) + .Select(JToken.Parse) .Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson)); } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 53d45261e..1b69c6646 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2324,7 +2324,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => i.IsLocalFile) .Select(i => System.IO.Path.GetDirectoryName(i.Path)) .Distinct(StringComparer.OrdinalIgnoreCase) - .SelectMany(i => directoryService.GetFilePaths(i)) + .SelectMany(directoryService.GetFilePaths) .ToList(); var deletedImages = ImageInfos diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index a87104cd6..ee2e5fcde 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.MediaEncoding.Probing .Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec)) .ToList(); - info.MediaAttachments = internalStreams.Select(s => GetMediaAttachment(s)) + info.MediaAttachments = internalStreams.Select(GetMediaAttachment) .Where(i => i != null) .ToList(); diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 398d47d5f..f4c69fe8f 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -297,7 +297,7 @@ namespace MediaBrowser.Model.Dlna int? inputAudioSampleRate = audioStream?.SampleRate; int? inputAudioBitDepth = audioStream?.BitDepth; - if (directPlayMethods.Count() > 0) + if (directPlayMethods.Any()) { string audioCodec = audioStream?.Codec; From bc1cc2d04ae0e823becf59964e5bdc5a74ae7741 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 17 Apr 2021 11:37:55 +0100 Subject: [PATCH 302/402] Remove unused using directives --- Emby.Dlna/ContentDirectory/ControlHandler.cs | 2 -- Emby.Dlna/Main/DlnaEntryPoint.cs | 1 - .../MediaReceiverRegistrarXmlBuilder.cs | 1 - Emby.Dlna/PlayTo/SsdpHttpClient.cs | 1 - .../AppBase/ConfigurationHelper.cs | 1 - Emby.Server.Implementations/Channels/ChannelManager.cs | 1 - .../Collections/CollectionManager.cs | 3 --- Emby.Server.Implementations/ConfigurationOptions.cs | 1 - .../HttpServer/Security/AuthService.cs | 1 - Emby.Server.Implementations/IStartupOptions.cs | 1 - Emby.Server.Implementations/Images/ArtistImageProvider.cs | 8 -------- .../Images/DynamicImageProvider.cs | 1 - Emby.Server.Implementations/Library/PathExtensions.cs | 2 -- Emby.Server.Implementations/Library/SearchEngine.cs | 1 - Emby.Server.Implementations/Library/UserDataManager.cs | 2 +- Emby.Server.Implementations/Library/UserViewManager.cs | 1 - Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 1 - .../LiveTv/EmbyTV/ItemDataProvider.cs | 2 -- .../LiveTv/EmbyTV/SeriesTimerManager.cs | 1 - .../LiveTv/Listings/XmlTvListingsProvider.cs | 1 - .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 3 --- .../LiveTv/TunerHosts/M3uParser.cs | 1 - .../Localization/LocalizationManager.cs | 1 - .../MediaEncoder/EncodingManager.cs | 1 - .../QuickConnect/QuickConnectManager.cs | 1 - .../ScheduledTasks/ScheduledTaskWorker.cs | 1 - .../ScheduledTasks/Tasks/ChapterImagesTask.cs | 2 +- .../ScheduledTasks/Tasks/PeopleValidationTask.cs | 2 +- .../ScheduledTasks/Tasks/PluginUpdateTask.cs | 1 - .../ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs | 2 +- Jellyfin.Api/Controllers/PluginsController.cs | 1 - Jellyfin.Api/Extensions/DtoExtensions.cs | 1 - Jellyfin.Api/Helpers/AudioHelper.cs | 3 +-- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 1 - Jellyfin.Data/Entities/HomeSection.cs | 3 +-- Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs | 1 - Jellyfin.Networking/Configuration/NetworkConfiguration.cs | 1 - .../Configuration/NetworkConfigurationExtensions.cs | 1 - Jellyfin.Server/Filters/ParameterObsoleteFilter.cs | 1 - Jellyfin.Server/Formatters/CssOutputFormatter.cs | 3 +-- .../Middleware/IpBasedAccessValidationMiddleware.cs | 2 -- Jellyfin.Server/Middleware/LanFilteringMiddleware.cs | 3 --- .../Migrations/Routines/DisableTranscodingThrottling.cs | 1 - Jellyfin.Server/Program.cs | 2 -- Jellyfin.Server/StartupOptions.cs | 3 --- MediaBrowser.Common/Cryptography/PasswordHash.cs | 1 - MediaBrowser.Common/Net/INetworkManager.cs | 1 - .../BaseItemManager/BaseItemManager.cs | 1 - .../BaseItemManager/IBaseItemManager.cs | 1 - MediaBrowser.Controller/Drawing/ImageHelper.cs | 3 --- MediaBrowser.Controller/Entities/Folder.cs | 1 - .../Events/Updates/PluginUninstalledEventArgs.cs | 1 - MediaBrowser.Controller/IServerApplicationHost.cs | 3 --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 -- MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs | 1 - MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs | 1 - MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 1 - .../MediaEncoding/MediaEncoderHelpers.cs | 5 ----- MediaBrowser.Controller/Playlists/Playlist.cs | 1 - MediaBrowser.Controller/Providers/IRemoteImageProvider.cs | 1 - .../Providers/IRemoteSearchProvider.cs | 1 - MediaBrowser.Controller/Subtitles/ISubtitleManager.cs | 1 - MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 1 - MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs | 1 - MediaBrowser.Model/Dto/NameIdPair.cs | 2 -- MediaBrowser.Model/LiveTv/TunerHostInfo.cs | 3 --- MediaBrowser.Model/Notifications/NotificationOptions.cs | 2 -- MediaBrowser.Providers/Manager/ProviderManager.cs | 1 - .../Plugins/AudioDb/AlbumImageProvider.cs | 1 - MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs | 1 - .../Plugins/AudioDb/ArtistImageProvider.cs | 1 - MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs | 1 - MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs | 2 -- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 4 ++-- .../Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs | 1 - .../Json/JsonNullableGuidConverterTests.cs | 1 - .../Jellyfin.Model.Tests/Extensions/StringHelperTests.cs | 1 - .../AudioBook/AudioBookListResolverTests.cs | 1 - .../AudioBook/AudioBookResolverTests.cs | 1 - .../Subtitles/SubtitleParserTests.cs | 1 - tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs | 1 - .../IO/ManagedFileSystemTests.cs | 1 - .../Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs | 2 -- tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs | 1 - tests/Jellyfin.Server.Tests/ParseNetworkTests.cs | 1 - .../Parsers/MusicArtistNfoParserTests.cs | 1 - 86 files changed, 9 insertions(+), 125 deletions(-) diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 713f95099..90ba601b4 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -7,7 +6,6 @@ using System.Linq; using System.Text; using System.Threading; using System.Xml; -using Emby.Dlna.Configuration; using Emby.Dlna.Didl; using Emby.Dlna.Service; using Jellyfin.Data.Entities; diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index d3e9a41ec..bdfe430cf 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.Linq; using System.Net.Http; using System.Net.Sockets; -using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs index 37840cd09..f3789a791 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using Emby.Dlna.Common; using Emby.Dlna.Service; -using MediaBrowser.Model.Dlna; namespace Emby.Dlna.MediaReceiverRegistrar { diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index e750f5bbc..d9f1ce490 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -2,7 +2,6 @@ using System; using System.Globalization; -using System.IO; using System.Net.Http; using System.Net.Mime; using System.Text; diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs index 3f7076383..29bac6634 100644 --- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs +++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs @@ -3,7 +3,6 @@ using System; using System.IO; using System.Linq; -using MediaBrowser.Common.Extensions; using MediaBrowser.Model.Serialization; namespace Emby.Server.Implementations.AppBase diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 87ebe960a..7324b0ee9 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index db532ce5b..e984afdba 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -8,11 +7,9 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Collections; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index cd9dbb1bd..01dc728c1 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Emby.Server.Implementations.HttpServer; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; namespace Emby.Server.Implementations diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs index 4a0fc8239..9afabf527 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 -using System; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Net; diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs index 0b823ff06..f719dc5f8 100644 --- a/Emby.Server.Implementations/IStartupOptions.cs +++ b/Emby.Server.Implementations/IStartupOptions.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 #nullable enable -using System; namespace Emby.Server.Implementations { diff --git a/Emby.Server.Implementations/Images/ArtistImageProvider.cs b/Emby.Server.Implementations/Images/ArtistImageProvider.cs index afa4ec7b1..e96b64595 100644 --- a/Emby.Server.Implementations/Images/ArtistImageProvider.cs +++ b/Emby.Server.Implementations/Images/ArtistImageProvider.cs @@ -2,20 +2,12 @@ using System; using System.Collections.Generic; -using System.Linq; -using Emby.Server.Implementations.Images; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Querying; namespace Emby.Server.Implementations.Images { diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs index 462eb03a8..50c531482 100644 --- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Emby.Server.Implementations.Images; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 6eaecff0f..770cf6bb0 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -2,8 +2,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Text.RegularExpressions; using MediaBrowser.Common.Providers; namespace Emby.Server.Implementations.Library diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 94602582b..bcdf854ca 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -12,7 +12,6 @@ using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Search; -using Microsoft.Extensions.Logging; using Genre = MediaBrowser.Controller.Entities.Genre; using Person = MediaBrowser.Controller.Entities.Person; diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index d16275b19..e8caea196 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -13,8 +13,8 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using Book = MediaBrowser.Controller.Entities.Book; using AudioBook = MediaBrowser.Controller.Entities.AudioBook; +using Book = MediaBrowser.Controller.Entities.Book; namespace Emby.Server.Implementations.Library { diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index b6b7ea949..ac041bcf6 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading; using Jellyfin.Data.Entities; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 91a21db60..c9d9cc49a 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -17,7 +17,6 @@ using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index c20b08088..1cac9cb96 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -4,9 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Text.Json; -using System.Threading.Tasks; using MediaBrowser.Common.Json; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs index da707fec6..b1259de23 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs @@ -2,7 +2,6 @@ using System; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 76c875737..6824aa442 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Threading; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 68173a0ef..1dcc78687 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -8,10 +8,8 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; @@ -19,7 +17,6 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 2af635492..cc30a516d 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 98de848bc..2fdc2b4d9 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -11,7 +11,6 @@ using MediaBrowser.Common.Json; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Localization diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index a9dab9138..031b5d2e7 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -15,7 +15,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.MediaEncoder diff --git a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs index 7bed06de3..22739a008 100644 --- a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs +++ b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs @@ -3,7 +3,6 @@ using System.Collections.Concurrent; using System.Globalization; using System.Linq; using System.Security.Cryptography; -using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 3cc2cefb9..9c0e92705 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -4,7 +4,6 @@ using System; using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index 649305fd5..2312c85d9 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -12,9 +12,9 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.ScheduledTasks { diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs index c384cf4bb..57d294a40 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Tasks; namespace Emby.Server.Implementations.ScheduledTasks { diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs index a69380cbb..11a5fb79f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Updates; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs index e470adcf4..51b620404 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs @@ -6,8 +6,8 @@ using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Library; using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Tasks; namespace Emby.Server.Implementations.ScheduledTasks { diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index 24285bfb9..adec86a10 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -12,7 +12,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Json; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Net; using MediaBrowser.Model.Plugins; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Extensions/DtoExtensions.cs b/Jellyfin.Api/Extensions/DtoExtensions.cs index e0c744325..06173315a 100644 --- a/Jellyfin.Api/Extensions/DtoExtensions.cs +++ b/Jellyfin.Api/Extensions/DtoExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs index 21ec2d32f..9c35d1ec1 100644 --- a/Jellyfin.Api/Helpers/AudioHelper.cs +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -1,5 +1,4 @@ -using System; -using System.Net.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Models.StreamingDtos; diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 751b48682..1bb504ad1 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; -using System.Net.Mime; using System.Security.Claims; using System.Text; using System.Threading; diff --git a/Jellyfin.Data/Entities/HomeSection.cs b/Jellyfin.Data/Entities/HomeSection.cs index 5adc52491..d03d0f7a8 100644 --- a/Jellyfin.Data/Entities/HomeSection.cs +++ b/Jellyfin.Data/Entities/HomeSection.cs @@ -1,5 +1,4 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; namespace Jellyfin.Data.Entities diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs index 730deccae..cc04d033a 100644 --- a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; namespace Jellyfin.Data.Entities.Libraries diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs index 91bf0015f..faf814c06 100644 --- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs +++ b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs @@ -1,7 +1,6 @@ #pragma warning disable CA1819 // Properties should not return arrays using System; -using MediaBrowser.Model.Configuration; namespace Jellyfin.Networking.Configuration { diff --git a/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs b/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs index e77b17ba9..8cbe398b0 100644 --- a/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs +++ b/Jellyfin.Networking/Configuration/NetworkConfigurationExtensions.cs @@ -1,4 +1,3 @@ -using Jellyfin.Networking.Configuration; using MediaBrowser.Common.Configuration; namespace Jellyfin.Networking.Configuration diff --git a/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs index e54044d0e..b9ce221f5 100644 --- a/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs +++ b/Jellyfin.Server/Filters/ParameterObsoleteFilter.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using Jellyfin.Api.Attributes; -using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; diff --git a/Jellyfin.Server/Formatters/CssOutputFormatter.cs b/Jellyfin.Server/Formatters/CssOutputFormatter.cs index e8dd48e4e..cfc9d1ad3 100644 --- a/Jellyfin.Server/Formatters/CssOutputFormatter.cs +++ b/Jellyfin.Server/Formatters/CssOutputFormatter.cs @@ -1,5 +1,4 @@ -using System; -using System.Text; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; diff --git a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs index 7d92bd7d3..0afcd61a0 100644 --- a/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Server/Middleware/IpBasedAccessValidationMiddleware.cs @@ -1,9 +1,7 @@ using System.Net; using System.Threading.Tasks; -using Jellyfin.Networking.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; namespace Jellyfin.Server.Middleware diff --git a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs index 8065054a1..67bf24d2a 100644 --- a/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Server/Middleware/LanFilteringMiddleware.cs @@ -1,9 +1,6 @@ -using System; -using System.Linq; using System.Net; using System.Threading.Tasks; using Jellyfin.Networking.Configuration; -using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index bf0225e98..378e88e25 100644 --- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -1,6 +1,5 @@ using System; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations.Routines diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 464e02419..c10b2ddb3 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -12,12 +12,10 @@ using System.Threading.Tasks; using CommandLine; using Emby.Server.Implementations; using Emby.Server.Implementations.IO; -using Jellyfin.Api.Controllers; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Extensions; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index 6d8210527..a1cecc8c6 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -1,10 +1,7 @@ -using System; using System.Collections.Generic; using CommandLine; using Emby.Server.Implementations; -using Emby.Server.Implementations.EntryPoints; using Emby.Server.Implementations.Udp; -using Emby.Server.Implementations.Updates; using MediaBrowser.Controller.Extensions; namespace Jellyfin.Server diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs index f2ecc4741..ec21d0580 100644 --- a/MediaBrowser.Common/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Text; namespace MediaBrowser.Common.Cryptography diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 012824f65..185df5b77 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Net; using System.Net.NetworkInformation; -using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Common.Net diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs index 31dd95402..a233c358e 100644 --- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Threading; -using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs index e1f5d05a6..8a8736427 100644 --- a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index 181f8e905..596fcbc8c 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -1,10 +1,7 @@ #pragma warning disable CS1591 #nullable enable -using System; -using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Drawing { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index cac5026f7..bdca5c0ee 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Text.Json.Serialization; diff --git a/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs index a111e6d82..0f27be9bb 100644 --- a/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs +++ b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs @@ -1,5 +1,4 @@ using Jellyfin.Data.Events; -using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Plugins; namespace MediaBrowser.Controller.Events.Updates diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 20bfa697e..6a65a8e47 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -3,10 +3,7 @@ using System; using System.Collections.Generic; using System.Net; -using System.Threading; -using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Plugins; using MediaBrowser.Model.System; using Microsoft.AspNetCore.Http; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 92b9a8c7e..1379efacb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -10,8 +10,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using Jellyfin.Data.Enums; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index dacd6dea6..d47a689f4 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -9,7 +9,6 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using MediaBrowser.Model.Session; diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 5cbb57990..05dd1a69b 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index cc8820f39..227c5f258 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -3,7 +3,6 @@ using System; using System.Globalization; using System.IO; -using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 281d50372..89e01c08b 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -1,10 +1,5 @@ #pragma warning disable CS1591 -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MediaBrowser.Model.IO; namespace MediaBrowser.Controller.MediaEncoding { diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 977b14cb0..a5b7363fb 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -14,7 +14,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Playlists diff --git a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs index ee8f5b860..de1631dcf 100644 --- a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; diff --git a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs index 9592baa7c..e401ed211 100644 --- a/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteSearchProvider.cs @@ -3,7 +3,6 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; namespace MediaBrowser.Controller.Providers { diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index feb26bc10..6d63286ef 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 205933ae2..36bf77c84 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -16,7 +16,6 @@ using MediaBrowser.Common.Json; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.MediaEncoding.Probing; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index 8a7c032c5..7b7744163 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; namespace MediaBrowser.MediaEncoding.Probing { diff --git a/MediaBrowser.Model/Dto/NameIdPair.cs b/MediaBrowser.Model/Dto/NameIdPair.cs index 7f18b4502..31516947f 100644 --- a/MediaBrowser.Model/Dto/NameIdPair.cs +++ b/MediaBrowser.Model/Dto/NameIdPair.cs @@ -1,8 +1,6 @@ #nullable disable #pragma warning disable CS1591 -using System; - namespace MediaBrowser.Model.Dto { public class NameIdPair diff --git a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs index 7d4bbb2d0..05576a0f8 100644 --- a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs +++ b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs @@ -1,9 +1,6 @@ #nullable disable #pragma warning disable CS1591 -using System; -using MediaBrowser.Model.Dto; - namespace MediaBrowser.Model.LiveTv { public class TunerHostInfo diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs index 94bb5d6e3..12e093b21 100644 --- a/MediaBrowser.Model/Notifications/NotificationOptions.cs +++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs @@ -5,8 +5,6 @@ using System; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; -using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.Users; namespace MediaBrowser.Model.Notifications { diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index b4b0b826f..3bb2c6f0b 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -25,7 +25,6 @@ using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; using Priority_Queue; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index 2adb11908..85a28747f 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -14,7 +14,6 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.AudioDb { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 00feeec1f..25bb3f9ce 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -19,7 +19,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.Plugins.AudioDb diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index b8095ff04..db8536cc9 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -14,7 +14,6 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Plugins.AudioDb { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index 59ecbc017..cbb61fa35 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -18,7 +18,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.Plugins.AudioDb diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index d35805a84..46d303890 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -6,9 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; -using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 833d1ae38..d22c1b50a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -7,8 +7,6 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using TMDbLib.Objects.Find; -using TMDbLib.Objects.Search; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -16,6 +14,8 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using TMDbLib.Objects.Find; +using TMDbLib.Objects.Search; namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { diff --git a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs index 1e1cde957..dbfad3c2f 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.Text.Json; using MediaBrowser.Common.Json.Converters; using Xunit; diff --git a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs index 22bc7afb9..cb3b66c4c 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs +++ b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.Text.Json; using MediaBrowser.Common.Json.Converters; using Xunit; diff --git a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs index 51633e157..5864a0509 100644 --- a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs +++ b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs @@ -1,4 +1,3 @@ -using System; using MediaBrowser.Model.Extensions; using Xunit; diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs index e5768b620..d9e77dd2e 100644 --- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookListResolverTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Emby.Naming.AudioBook; using Emby.Naming.Common; diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs index ad63adadc..53b35c2d6 100644 --- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Emby.Naming.AudioBook; using Emby.Naming.Common; diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs index f3abacb4f..2446660f3 100644 --- a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs @@ -1,4 +1,3 @@ -using System; using Emby.Naming.Common; using Emby.Naming.Subtitles; using Xunit; diff --git a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs index d34f65409..2f173b0ce 100644 --- a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs @@ -1,4 +1,3 @@ -using System; using Emby.Naming.Common; using Emby.Naming.Video; using MediaBrowser.Model.Entities; diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 5a535ac51..614a68975 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; diff --git a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs index 3cbd638f9..0ade345a1 100644 --- a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs @@ -1,8 +1,6 @@ using System.IO; using System.Reflection; -using System.Text.Json; using System.Threading.Tasks; -using MediaBrowser.Model.Branding; using Xunit; using Xunit.Abstractions; diff --git a/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs index 4e5d0fcb6..0a463cfa3 100644 --- a/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs +++ b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Reflection; using Emby.Server.Implementations; -using Jellyfin.Server; using MediaBrowser.Controller; using MediaBrowser.Model.IO; using Microsoft.Extensions.Configuration; diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs index 0b714e80a..146b16cf9 100644 --- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs +++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs @@ -1,4 +1,3 @@ -using System; using System.Globalization; using System.Text; using Jellyfin.Networking.Configuration; diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs index 3d8e13e96..8ca3dd96e 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicArtistNfoParserTests.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.Audio; From e841922ffd9211cfeee5cca1bda4c139cbda9379 Mon Sep 17 00:00:00 2001 From: Stephen Moore Date: Sat, 17 Apr 2021 17:21:40 +0100 Subject: [PATCH 303/402] Fix ArgumentOutOfRangeException scanning AudioBooks AudioResolver.ResolveMultipleAudio method can attempt to access the first item in a List without checking if the list is empty which throws an ArgumentOutOfRangeException and stops the 'Scan Library' process. --- .../Library/Resolvers/Audio/AudioResolver.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 90b6a8a7d..4ad84579d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -201,6 +201,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio continue; } + if (resolvedItem.Files.Count == 0) + { + continue; + } + var firstMedia = resolvedItem.Files[0]; var libraryItem = new T From 351b987982559b518de7654e85bb076b32ae0d10 Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 18 Apr 2021 12:34:33 +0200 Subject: [PATCH 304/402] Add Person to TypedBaseItems if it's new --- .../Manager/MetadataService.cs | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 437b43eca..f12586665 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -223,13 +223,13 @@ namespace MediaBrowser.Providers.Manager var baseItem = result.Item; LibraryManager.UpdatePeople(baseItem, result.People); - await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false); + await SavePeopleMetadataAsync(result.People, cancellationToken).ConfigureAwait(false); } await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false); } - private async Task SavePeopleMetadataAsync(List people, LibraryOptions libraryOptions, CancellationToken cancellationToken) + private async Task SavePeopleMetadataAsync(IEnumerable people, CancellationToken cancellationToken) { var personsToSave = new List(); @@ -237,39 +237,44 @@ namespace MediaBrowser.Providers.Manager { cancellationToken.ThrowIfCancellationRequested(); - if (person.ProviderIds.Count > 0 || !string.IsNullOrWhiteSpace(person.ImageUrl)) + var itemUpdateType = ItemUpdateType.MetadataDownload; + var saveEntity = false; + var personEntity = LibraryManager.GetPerson(person.Name); + + // if PresentationUniqueKey is empty it's likely a new item. + if (string.IsNullOrEmpty(personEntity.PresentationUniqueKey)) { - var itemUpdateType = ItemUpdateType.MetadataDownload; - var saveEntity = false; - var personEntity = LibraryManager.GetPerson(person.Name); - foreach (var id in person.ProviderIds) - { - if (!string.Equals(personEntity.GetProviderId(id.Key), id.Value, StringComparison.OrdinalIgnoreCase)) - { - personEntity.SetProviderId(id.Key, id.Value); - saveEntity = true; - } - } + personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); + saveEntity = true; + } - if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) + foreach (var id in person.ProviderIds) + { + if (!string.Equals(personEntity.GetProviderId(id.Key), id.Value, StringComparison.OrdinalIgnoreCase)) { - personEntity.SetImage( - new ItemImageInfo - { - Path = person.ImageUrl, - Type = ImageType.Primary - }, - 0); - + personEntity.SetProviderId(id.Key, id.Value); saveEntity = true; - itemUpdateType = ItemUpdateType.ImageUpdate; } + } - if (saveEntity) - { - personsToSave.Add(personEntity); - await LibraryManager.RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); - } + if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) + { + personEntity.SetImage( + new ItemImageInfo + { + Path = person.ImageUrl, + Type = ImageType.Primary + }, + 0); + + saveEntity = true; + itemUpdateType = ItemUpdateType.ImageUpdate; + } + + if (saveEntity) + { + personsToSave.Add(personEntity); + await LibraryManager.RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); } } From 8933389753cc95413576b2fcfdf2ccb160464ad3 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 19 Apr 2021 10:49:52 +0200 Subject: [PATCH 305/402] Respect user settings for transcode and remux --- .../Library/MediaSourceManager.cs | 12 +++++++++--- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index b2943020c..d0b85f07d 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -199,10 +199,15 @@ namespace Emby.Server.Implementations.Library { source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding); } + else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) + { + source.SupportsTranscoding = user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding); + source.SupportsDirectStream = user.HasPermission(PermissionKind.EnablePlaybackRemuxing); + } } } - return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder).ToList(); + return SortMediaSources(list); } public MediaProtocol GetPathProtocol(string path) @@ -436,7 +441,7 @@ namespace Emby.Server.Implementations.Library } } - private static IEnumerable SortMediaSources(IEnumerable sources) + private static List SortMediaSources(IEnumerable sources) { return sources.OrderBy(i => { @@ -451,8 +456,9 @@ namespace Emby.Server.Implementations.Library { var stream = i.VideoStream; - return stream == null || stream.Width == null ? 0 : stream.Width.Value; + return stream?.Width ?? 0; }) + .Where(i => i.Type != MediaSourceType.Placeholder) .ToList(); } diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index f07271821..295cfaf08 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -309,7 +309,7 @@ namespace Jellyfin.Api.Helpers { if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding) && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding) - && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) + && user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) { options.ForceDirectStream = true; } From a9ca3c8b0165d949f63cf3a16a99b62b72a6606d Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 19 Apr 2021 11:38:27 +0200 Subject: [PATCH 306/402] Fix notification disabled users list --- MediaBrowser.Model/Notifications/NotificationOptions.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs index 12e093b21..09beb2ef7 100644 --- a/MediaBrowser.Model/Notifications/NotificationOptions.cs +++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs @@ -93,16 +93,17 @@ namespace MediaBrowser.Model.Notifications { NotificationOption opt = GetOptions(notificationType); - return opt == null || - !opt.DisabledServices.Contains(service, StringComparer.OrdinalIgnoreCase); + return opt == null + || !opt.DisabledServices.Contains(service, StringComparer.OrdinalIgnoreCase); } public bool IsEnabledToMonitorUser(string type, Guid userId) { NotificationOption opt = GetOptions(type); - return opt != null && opt.Enabled && - !opt.DisabledMonitorUsers.Contains(userId.ToString(string.Empty), StringComparer.OrdinalIgnoreCase); + return opt != null + && opt.Enabled + && !opt.DisabledMonitorUsers.Contains(userId.ToString("N"), StringComparer.OrdinalIgnoreCase); } public bool IsEnabledToSendToUser(string type, string userId, User user) From 90b941b3f6646d34f831a2921bc5919bfe9871c0 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 19 Apr 2021 14:59:24 +0400 Subject: [PATCH 307/402] Add review changes --- .../Plugins/Tmdb/TmdbClientManager.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index 3aa673274..69f964937 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -145,16 +145,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// A comma-separated list of image languages. /// The cancellation token. /// The TMDb tv show episode group information or null if not found. - public async Task GetSeriesGroupAsync(int tvShowId, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken) + private async Task GetSeriesGroupAsync(int tvShowId, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken) { - var key = $"group-{tvShowId.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}"; - if (_memoryCache.TryGetValue(key, out TvGroupCollection group)) - { - return group; - } - - await EnsureClientConfigAsync().ConfigureAwait(false); - TvGroupType? groupType = displayOrder == "absolute" ? TvGroupType.Absolute : displayOrder == "dvd" ? TvGroupType.DVD : @@ -165,8 +157,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb return null; } - var series = await GetSeriesAsync(tvShowId, language, imageLanguages, cancellationToken); - var episodeGroupId = series.EpisodeGroups.Results.Find(g => g.Type == groupType)?.Id; + var key = $"group-{tvShowId.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}"; + if (_memoryCache.TryGetValue(key, out TvGroupCollection group)) + { + return group; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + var series = await GetSeriesAsync(tvShowId, language, imageLanguages, cancellationToken).ConfigureAwait(false); + var episodeGroupId = series?.EpisodeGroups.Results.Find(g => g.Type == groupType)?.Id; if (episodeGroupId == null) { @@ -246,6 +246,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb if (group != null) { var season = group.Groups.Find(s => s.Order == seasonNumber); + // Episode order starts at 0 var ep = season?.Episodes.Find(e => e.Order == episodeNumber - 1); if (ep != null) { From c68f6163774e4d6534a5a68d2c0f6b7435240ed3 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 19 Apr 2021 12:36:30 +0100 Subject: [PATCH 308/402] Flip fields --- Emby.Dlna/DlnaManager.cs | 2 +- tests/Jellyfin.Dlna.Tests/ProfileTester.cs | 120 +++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/Jellyfin.Dlna.Tests/ProfileTester.cs diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 9ab324038..88b0e1195 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -107,7 +107,7 @@ namespace Emby.Dlna } var profile = GetProfiles() - .FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification)); + .FirstOrDefault(i => i.Identification != null && IsMatch(i.Identification, deviceInfo)); if (profile != null) { diff --git a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs new file mode 100644 index 000000000..83638c7c4 --- /dev/null +++ b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Emby.Dlna.PlayTo; +using MediaBrowser.Model.Dlna; +using Xunit; + +namespace Jellyfin.Dlna.Tests +{ + public class ProfileTester + { + private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) + { + if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) + { + if (deviceInfo.FriendlyName == null || !IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) + { + if (deviceInfo.Manufacturer == null || !IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl)) + { + if (deviceInfo.ManufacturerUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(profileInfo.ModelDescription)) + { + if (deviceInfo.ModelDescription == null || !IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(profileInfo.ModelName)) + { + if (deviceInfo.ModelName == null || !IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(profileInfo.ModelNumber)) + { + if (deviceInfo.ModelNumber == null || !IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(profileInfo.ModelUrl)) + { + if (deviceInfo.ModelUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(profileInfo.SerialNumber)) + { + if (deviceInfo.SerialNumber == null || !IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) + { + return false; + } + } + + return true; + } + + private bool IsRegexOrSubstringMatch(string input, string pattern) + { + return input.Contains(pattern, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + } + + [Fact] + public void Test_Profile_Matches() + { + var source = new DeviceInfo() + { + Name = "HelloWorld" + }; + + var dest = new DeviceProfile() + { + Name = "Test Subject 1", + FriendlyName = "HelloWorld", + Manufacturer = "LG Electronics", + ManufacturerUrl = "http://www.lge.com", + ModelDescription = "LG WebOSTV DMRplus", + ModelName = "LG TV", + ModelNumber = "1.0", + Identification = new DeviceIdentification() + { + FriendlyName = "HelloWorld", + Manufacturer = "LG Electronics", + ManufacturerUrl = "http://www.lge.com", + ModelDescription = "LG WebOSTV DMRplus", + ModelName = "LG TV", + ModelNumber = "1.0", + } + }; + + Assert.True(IsMatch(dest.Identification, source.ToDeviceIdentification())); + } + } +} From 586e1fc58a52d343714958af713d102300ccfde0 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 19 Apr 2021 13:51:55 +0200 Subject: [PATCH 309/402] use IF NOT EXISTS in migration --- Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs b/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs index 2521d9952..6343c422d 100644 --- a/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs +++ b/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs @@ -41,9 +41,9 @@ namespace Jellyfin.Server.Migrations.Routines var databasePath = Path.Join(_serverApplicationPaths.DataPath, DbFilename); using var connection = SQLite3.Open(databasePath, ConnectionFlags.ReadWrite, null); _logger.LogInformation("Creating index idx_TypedBaseItemsUserDataKeyType"); - connection.Execute("CREATE INDEX idx_TypedBaseItemsUserDataKeyType ON TypedBaseItems(UserDataKey, Type);"); + connection.Execute("CREATE INDEX IF NOT EXISTS idx_TypedBaseItemsUserDataKeyType ON TypedBaseItems(UserDataKey, Type);"); _logger.LogInformation("Creating index idx_PeopleNameListOrder"); - connection.Execute("CREATE INDEX idx_PeopleNameListOrder ON People(Name, ListOrder);"); + connection.Execute("CREATE INDEX IF NOT EXISTS idx_PeopleNameListOrder ON People(Name, ListOrder);"); } } -} \ No newline at end of file +} From 442bc8671bfd870641c67c54d1e365cc371bc3a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 12:08:51 +0000 Subject: [PATCH 310/402] Bump AutoFixture.AutoMoq from 4.15.0 to 4.16.0 Bumps [AutoFixture.AutoMoq](https://github.com/AutoFixture/AutoFixture) from 4.15.0 to 4.16.0. - [Release notes](https://github.com/AutoFixture/AutoFixture/releases) - [Commits](https://github.com/AutoFixture/AutoFixture/compare/v4.15.0...v4.16.0) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 1306783c9..00c40466e 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 38de05ad0..486899f4f 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -23,7 +23,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index d7e56ff13..437175f95 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 51d6333b7..e8fa55d9b 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -11,7 +11,7 @@ - + From 64071873a0a68dd12a4a4995c7d9e4713763aab3 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 19 Apr 2021 16:51:44 +0400 Subject: [PATCH 311/402] Use StringComparison.Ordinal --- MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index 69f964937..05e5d3ced 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -148,8 +148,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb private async Task GetSeriesGroupAsync(int tvShowId, string displayOrder, string language, string imageLanguages, CancellationToken cancellationToken) { TvGroupType? groupType = - displayOrder == "absolute" ? TvGroupType.Absolute : - displayOrder == "dvd" ? TvGroupType.DVD : + string.Equals(displayOrder, "absolute", StringComparison.Ordinal) ? TvGroupType.Absolute : + string.Equals(displayOrder, "dvd", StringComparison.Ordinal) ? TvGroupType.DVD : null; if (groupType == null) From 95b733ad4cbbe35b638de51e0b84d2794d28c34d Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 19 Apr 2021 14:07:14 +0100 Subject: [PATCH 312/402] reworked code --- Emby.Dlna/DlnaManager.cs | 112 +++++++++-------- tests/Jellyfin.Dlna.Tests/ProfileTester.cs | 138 +++++++++------------ 2 files changed, 116 insertions(+), 134 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 88b0e1195..4a50136cf 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -106,19 +106,28 @@ namespace Emby.Dlna throw new ArgumentNullException(nameof(deviceInfo)); } - var profile = GetProfiles() - .FirstOrDefault(i => i.Identification != null && IsMatch(i.Identification, deviceInfo)); + try + { + var profile = GetProfiles() + .FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification)); - if (profile != null) - { - _logger.LogDebug("Found matching device profile: {0}", profile.Name); + if (profile != null) + { + _logger.LogDebug("Found matching device profile: {0}", profile.Name); + } + else + { + LogUnmatchedProfile(deviceInfo); + } + + return profile; } - else + catch (ArgumentException ex) { - LogUnmatchedProfile(deviceInfo); + _logger.LogError(ex, "Error in profile comparison."); } - return profile; + return null; } private void LogUnmatchedProfile(DeviceIdentification profile) @@ -138,85 +147,82 @@ namespace Emby.Dlna _logger.LogInformation(builder.ToString()); } - private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) + /// + /// Attempts to match a device with a profile. + /// Rules: + /// - If the profile field has no value, the field matches irregardless of its contents. + /// - the profile field can be an exact match, or a reg exp. + /// + /// The of the device. + /// The of the profile. + /// True if they match. + public static bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) { - if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) + if (!IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) { - if (deviceInfo.FriendlyName == null || !IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) - { - return false; - } + return false; } - if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) + if (!IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) { - if (deviceInfo.Manufacturer == null || !IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) - { - return false; - } + return false; } - if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl)) + if (!IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) { - if (deviceInfo.ManufacturerUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) - { - return false; - } + return false; } - if (!string.IsNullOrEmpty(profileInfo.ModelDescription)) + if (!IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) { - if (deviceInfo.ModelDescription == null || !IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) - { - return false; - } + return false; } - if (!string.IsNullOrEmpty(profileInfo.ModelName)) + if (!IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)) { - if (deviceInfo.ModelName == null || !IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)) - { - return false; - } + return false; } - if (!string.IsNullOrEmpty(profileInfo.ModelNumber)) + if (!IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) { - if (deviceInfo.ModelNumber == null || !IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) - { - return false; - } + return false; } - if (!string.IsNullOrEmpty(profileInfo.ModelUrl)) + if (!IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) { - if (deviceInfo.ModelUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) - { - return false; - } + return false; } - if (!string.IsNullOrEmpty(profileInfo.SerialNumber)) + if (!IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) { - if (deviceInfo.SerialNumber == null || !IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) - { - return false; - } + return false; } return true; } - private bool IsRegexOrSubstringMatch(string input, string pattern) + public static bool IsRegexOrSubstringMatch(string input, string pattern) { + if (string.IsNullOrEmpty(pattern)) + { + // In profile identification: An empty pattern matches anything. + return true; + } + + if (string.IsNullOrEmpty(input)) + { + // The profile contains a value, and the device doesn't. + return false; + } + try { - return input.Contains(pattern, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + return input.Equals(pattern, StringComparison.OrdinalIgnoreCase) + || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } catch (ArgumentException ex) { - _logger.LogError(ex, "Error evaluating regex pattern {Pattern}", pattern); - return false; + throw new ArgumentException("Error evaluating regex pattern " + pattern, ex); } } diff --git a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs index 83638c7c4..cc7cf92e7 100644 --- a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs +++ b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Emby.Dlna; using Emby.Dlna.PlayTo; using MediaBrowser.Model.Dlna; using Xunit; @@ -12,92 +13,23 @@ namespace Jellyfin.Dlna.Tests { public class ProfileTester { - private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) - { - if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) - { - if (deviceInfo.FriendlyName == null || !IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) - { - if (deviceInfo.Manufacturer == null || !IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl)) - { - if (deviceInfo.ManufacturerUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.ModelDescription)) - { - if (deviceInfo.ModelDescription == null || !IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.ModelName)) - { - if (deviceInfo.ModelName == null || !IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.ModelNumber)) - { - if (deviceInfo.ModelNumber == null || !IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.ModelUrl)) - { - if (deviceInfo.ModelUrl == null || !IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(profileInfo.SerialNumber)) - { - if (deviceInfo.SerialNumber == null || !IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) - { - return false; - } - } - - return true; - } - - private bool IsRegexOrSubstringMatch(string input, string pattern) - { - return input.Contains(pattern, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - } - [Fact] public void Test_Profile_Matches() { - var source = new DeviceInfo() + var device = new DeviceInfo() { - Name = "HelloWorld" + Name = "My Device", + Manufacturer = "LG Electronics", + ManufacturerUrl = "http://www.lge.com", + ModelDescription = "LG WebOSTV DMRplus", + ModelName = "LG TV", + ModelNumber = "1.0", }; - var dest = new DeviceProfile() + var profile = new DeviceProfile() { - Name = "Test Subject 1", - FriendlyName = "HelloWorld", + Name = "Test Profile", + FriendlyName = "My Device", Manufacturer = "LG Electronics", ManufacturerUrl = "http://www.lge.com", ModelDescription = "LG WebOSTV DMRplus", @@ -105,7 +37,7 @@ namespace Jellyfin.Dlna.Tests ModelNumber = "1.0", Identification = new DeviceIdentification() { - FriendlyName = "HelloWorld", + FriendlyName = "My Device", Manufacturer = "LG Electronics", ManufacturerUrl = "http://www.lge.com", ModelDescription = "LG WebOSTV DMRplus", @@ -114,7 +46,51 @@ namespace Jellyfin.Dlna.Tests } }; - Assert.True(IsMatch(dest.Identification, source.ToDeviceIdentification())); + Assert.True(DlnaManager.IsMatch(device.ToDeviceIdentification(), profile.Identification)); + + var profile2 = new DeviceProfile() + { + Name = "Test Profile", + FriendlyName = "My Device", + Identification = new DeviceIdentification() + { + FriendlyName = "My Device", + } + }; + + Assert.True(DlnaManager.IsMatch(device.ToDeviceIdentification(), profile2.Identification)); + } + + [Fact] + public void Test_Profile_NoMatch() + { + var device = new DeviceInfo() + { + Name = "My Device", + Manufacturer = "JVC" + }; + + var profile = new DeviceProfile() + { + Name = "Test Profile", + FriendlyName = "My Device", + Manufacturer = "LG Electronics", + ManufacturerUrl = "http://www.lge.com", + ModelDescription = "LG WebOSTV DMRplus", + ModelName = "LG TV", + ModelNumber = "1.0", + Identification = new DeviceIdentification() + { + FriendlyName = "My Device", + Manufacturer = "LG Electronics", + ManufacturerUrl = "http://www.lge.com", + ModelDescription = "LG WebOSTV DMRplus", + ModelName = "LG TV", + ModelNumber = "1.0", + } + }; + + Assert.False(DlnaManager.IsMatch(device.ToDeviceIdentification(), profile.Identification)); } } } From dc628d1e1c18f169e5c3fc9da3e257967149ca0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 13:07:31 +0000 Subject: [PATCH 313/402] Bump AutoFixture.Xunit2 from 4.15.0 to 4.16.0 Bumps [AutoFixture.Xunit2](https://github.com/AutoFixture/AutoFixture) from 4.15.0 to 4.16.0. - [Release notes](https://github.com/AutoFixture/AutoFixture/releases) - [Commits](https://github.com/AutoFixture/AutoFixture/compare/v4.15.0...v4.16.0) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 00c40466e..050d4c040 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 437175f95..0de92249a 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index e8fa55d9b..9e60dbcd9 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -12,7 +12,7 @@ - + From 4449217f8ff736e98a50fc686331c5240f6fe33c Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 19 Apr 2021 14:24:58 +0100 Subject: [PATCH 314/402] simplified isMatch --- Emby.Dlna/DlnaManager.cs | 49 +++++++--------------------------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 4a50136cf..374e04210 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -158,47 +158,14 @@ namespace Emby.Dlna /// True if they match. public static bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) { - if (!IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) - { - return false; - } - - if (!IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) - { - return false; - } - - if (!IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) - { - return false; - } - - if (!IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) - { - return false; - } - - if (!IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)) - { - return false; - } - - if (!IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) - { - return false; - } - - if (!IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) - { - return false; - } - - if (!IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) - { - return false; - } - - return true; + return IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName) + && IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer) + && IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl) + && IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription) + && IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName) + && IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber) + && IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl) + && IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber); } public static bool IsRegexOrSubstringMatch(string input, string pattern) From 500c2e5224f0b242cdcbeb0da9176f4e856185e6 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 19 Apr 2021 22:37:24 +0200 Subject: [PATCH 315/402] Switch from HttpClientHandler to SocketsHttpHandler SocketsHttpHandler is the default for .Net Core 2.1 and newer Set RequestHeaderEncoding to UTF-8 by default --- Jellyfin.Server/Startup.cs | 14 ++++++++++++-- .../Net/DefaultHttpClientHandler.cs | 19 ------------------- 2 files changed, 12 insertions(+), 21 deletions(-) delete mode 100644 MediaBrowser.Common/Net/DefaultHttpClientHandler.cs diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index e56e61092..f75139884 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -1,5 +1,9 @@ +using System; +using System.Net; +using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mime; +using System.Text; using Jellyfin.Networking.Configuration; using Jellyfin.Server.Extensions; using Jellyfin.Server.Implementations; @@ -67,6 +71,12 @@ namespace Jellyfin.Server var acceptJsonHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json, 1.0); var acceptXmlHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Xml, 0.9); var acceptAnyHeader = new MediaTypeWithQualityHeaderValue("*/*", 0.8); + Func defaultHttpClientHandlerDelegate = (_) => new SocketsHttpHandler() + { + AutomaticDecompression = DecompressionMethods.All, + RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8 + }; + services .AddHttpClient(NamedClient.Default, c => { @@ -75,7 +85,7 @@ namespace Jellyfin.Server c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader); c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader); }) - .ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler()); + .ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate); services.AddHttpClient(NamedClient.MusicBrainz, c => { @@ -84,7 +94,7 @@ namespace Jellyfin.Server c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader); c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader); }) - .ConfigurePrimaryHttpMessageHandler(x => new DefaultHttpClientHandler()); + .ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate); services.AddHealthChecks() .AddDbContextCheck(); diff --git a/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs b/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs deleted file mode 100644 index f1c5f2477..000000000 --- a/MediaBrowser.Common/Net/DefaultHttpClientHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Net; -using System.Net.Http; - -namespace MediaBrowser.Common.Net -{ - /// - /// Default http client handler. - /// - public class DefaultHttpClientHandler : HttpClientHandler - { - /// - /// Initializes a new instance of the class. - /// - public DefaultHttpClientHandler() - { - AutomaticDecompression = DecompressionMethods.All; - } - } -} From dcd6ab769bb2bb1d45d1dae993e53ff2b7c1177b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 19 Apr 2021 23:52:58 +0200 Subject: [PATCH 316/402] ProviderManager: fix discard and 2 warnings --- MediaBrowser.Providers/Manager/ProviderManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 3bb2c6f0b..f078f9cef 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -1021,26 +1021,26 @@ namespace MediaBrowser.Providers.Manager // TODO: Need to hunt down the conditions for this happening _activeRefreshes.AddOrUpdate( id, - (_) => throw new Exception( + (_) => throw new InvalidOperationException( string.Format( CultureInfo.InvariantCulture, "Cannot update refresh progress of item '{0}' ({1}) because a refresh for this item is not running", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture))), - (_, __) => progress); + (_, _) => progress); RefreshProgress?.Invoke(this, new GenericEventArgs>(new Tuple(item, progress))); } /// - public void QueueRefresh(Guid id, MetadataRefreshOptions options, RefreshPriority priority) + public void QueueRefresh(Guid itemId, MetadataRefreshOptions options, RefreshPriority priority) { if (_disposed) { return; } - _refreshQueue.Enqueue(new Tuple(id, options), (int)priority); + _refreshQueue.Enqueue(new Tuple(itemId, options), (int)priority); lock (_refreshQueueLock) { From 06b8cf42d1d1edc424eaccb487590bc6a695ccba Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 20 Apr 2021 08:59:15 +0200 Subject: [PATCH 317/402] Fix TMDb Person Provider --- .../Tmdb/People/TmdbPersonImageProvider.cs | 53 +++++++++---------- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 18 +++---- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index 3f57c4bc4..0ea3f14a6 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -49,37 +49,36 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var person = (Person)item; - var personTmdbId = Convert.ToInt32(person.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (personTmdbId > 0) + if (!person.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId)) { - var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false); - if (personResult?.Images?.Profiles == null) - { - return Enumerable.Empty(); - } - - var remoteImages = new List(); - var language = item.GetPreferredMetadataLanguage(); - - for (var i = 0; i < personResult.Images.Profiles.Count; i++) - { - var image = personResult.Images.Profiles[i]; - remoteImages.Add(new RemoteImageInfo - { - ProviderName = Name, - Type = ImageType.Primary, - Width = image.Width, - Height = image.Height, - Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language), - Url = _tmdbClientManager.GetProfileUrl(image.FilePath) - }); - } - - return remoteImages.OrderByLanguageDescending(language); + return Enumerable.Empty(); } - return Enumerable.Empty(); + var personResult = await _tmdbClientManager.GetPersonAsync(Convert.ToInt32(personTmdbId), cancellationToken).ConfigureAwait(false); + if (personResult?.Images?.Profiles == null) + { + return Enumerable.Empty(); + } + + var remoteImages = new RemoteImageInfo[personResult.Images.Profiles.Count]; + var language = item.GetPreferredMetadataLanguage(); + + for (var i = 0; i < personResult.Images.Profiles.Count; i++) + { + var image = personResult.Images.Profiles[i]; + remoteImages[i] = new RemoteImageInfo + { + ProviderName = Name, + Type = ImageType.Primary, + Width = image.Width, + Height = image.Height, + Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language), + Url = _tmdbClientManager.GetProfileUrl(image.FilePath) + }; + } + + return remoteImages.OrderByLanguageDescending(language); } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 4384c203e..cf6a36275 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -30,11 +30,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People public async Task> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken) { - var personTmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - - if (personTmdbId <= 0) + if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId)) { - var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false); + var personResult = await _tmdbClientManager.GetPersonAsync(Convert.ToInt32(personTmdbId), cancellationToken).ConfigureAwait(false); if (personResult != null) { @@ -51,19 +49,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People } result.SetProviderId(MetadataProvider.Tmdb, personResult.Id.ToString(CultureInfo.InvariantCulture)); - result.SetProviderId(MetadataProvider.Imdb, personResult.ExternalIds.ImdbId); + if (!string.IsNullOrEmpty(personResult.ExternalIds.ImdbId)) + { + result.SetProviderId(MetadataProvider.Imdb, personResult.ExternalIds.ImdbId); + } return new[] { result }; } } - // TODO why? Because of the old rate limit? - if (searchInfo.IsAutomated) - { - // Don't hammer moviedb searching by name - return Enumerable.Empty(); - } - var personSearchResult = await _tmdbClientManager.SearchPersonAsync(searchInfo.Name, cancellationToken).ConfigureAwait(false); var remoteSearchResults = new List(); From 092c610fbf8268d5ff4518c2a6882764b89183d1 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 20 Apr 2021 08:26:41 +0100 Subject: [PATCH 318/402] Update Emby.Dlna/DlnaManager.cs Co-authored-by: Claus Vium --- Emby.Dlna/DlnaManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 374e04210..82dc30fb5 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -113,7 +113,7 @@ namespace Emby.Dlna if (profile != null) { - _logger.LogDebug("Found matching device profile: {0}", profile.Name); + _logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name); } else { From cbb855e65fd56d44b6e5714dd6843bb1d9dee1b8 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 20 Apr 2021 13:24:15 +0200 Subject: [PATCH 319/402] Check for empty string when migrating displaypreferences --- .../Migrations/Routines/MigrateDisplayPreferencesDb.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs index 07829c696..5dc8c90d4 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs @@ -126,13 +126,13 @@ namespace Jellyfin.Server.Migrations.Routines ShowSidebar = dto.ShowSidebar, ScrollDirection = dto.ScrollDirection, ChromecastVersion = chromecastVersion, - SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length) + SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length) && !string.IsNullOrEmpty(length) ? int.Parse(length, CultureInfo.InvariantCulture) : 30000, - SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) + SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && !string.IsNullOrEmpty(length) ? int.Parse(length, CultureInfo.InvariantCulture) : 10000, - EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) + EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) && !string.IsNullOrEmpty(enabled) ? bool.Parse(enabled) : true, DashboardTheme = dto.CustomPrefs.TryGetValue("dashboardtheme", out var theme) ? theme : string.Empty, From c0ea56a10dd085eaa363ec02122a119b07ecc0f8 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 20 Apr 2021 13:35:08 +0200 Subject: [PATCH 320/402] use int.Parse --- .../Plugins/Tmdb/People/TmdbPersonImageProvider.cs | 2 +- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index 0ea3f14a6..bf42ceade 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -55,7 +55,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People return Enumerable.Empty(); } - var personResult = await _tmdbClientManager.GetPersonAsync(Convert.ToInt32(personTmdbId), cancellationToken).ConfigureAwait(false); + var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), cancellationToken).ConfigureAwait(false); if (personResult?.Images?.Profiles == null) { return Enumerable.Empty(); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index cf6a36275..1757c8267 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People { if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId)) { - var personResult = await _tmdbClientManager.GetPersonAsync(Convert.ToInt32(personTmdbId), cancellationToken).ConfigureAwait(false); + var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), cancellationToken).ConfigureAwait(false); if (personResult != null) { From 4d7c1fbdca44eb6e892b104b765b86e146ac9765 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 20 Apr 2021 13:43:05 +0200 Subject: [PATCH 321/402] use int.Parse --- .../Migrations/Routines/MigrateDisplayPreferencesDb.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs index 5dc8c90d4..e25d29122 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs @@ -126,11 +126,11 @@ namespace Jellyfin.Server.Migrations.Routines ShowSidebar = dto.ShowSidebar, ScrollDirection = dto.ScrollDirection, ChromecastVersion = chromecastVersion, - SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length) && !string.IsNullOrEmpty(length) - ? int.Parse(length, CultureInfo.InvariantCulture) + SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length) && int.TryParse(length, out var skipForwardLength) + ? skipForwardLength : 30000, - SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && !string.IsNullOrEmpty(length) - ? int.Parse(length, CultureInfo.InvariantCulture) + SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && !string.IsNullOrEmpty(length) && int.TryParse(length, out var skipBackwardLength) + ? skipBackwardLength : 10000, EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) && !string.IsNullOrEmpty(enabled) ? bool.Parse(enabled) From a99caa0daae7c47afdf7d0cf5cf182976211ffa2 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 20 Apr 2021 18:04:16 +0100 Subject: [PATCH 322/402] Changed testing --- Emby.Dlna/DlnaManager.cs | 15 +++------- .../Jellyfin.Dlna.Tests.csproj | 1 + tests/Jellyfin.Dlna.Tests/ProfileTester.cs | 29 +++++++++++++------ 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 82dc30fb5..e80f1aaee 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -156,7 +156,7 @@ namespace Emby.Dlna /// The of the device. /// The of the profile. /// True if they match. - public static bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) + public bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) { return IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName) && IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer) @@ -168,7 +168,7 @@ namespace Emby.Dlna && IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber); } - public static bool IsRegexOrSubstringMatch(string input, string pattern) + private bool IsRegexOrSubstringMatch(string input, string pattern) { if (string.IsNullOrEmpty(pattern)) { @@ -182,15 +182,8 @@ namespace Emby.Dlna return false; } - try - { - return input.Equals(pattern, StringComparison.OrdinalIgnoreCase) - || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - } - catch (ArgumentException ex) - { - throw new ArgumentException("Error evaluating regex pattern " + pattern, ex); - } + return input.Equals(pattern, StringComparison.OrdinalIgnoreCase) + || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } public DeviceProfile GetProfile(IHeaderDictionary headers) diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index 850db1c75..a1255a858 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs index cc7cf92e7..3676cc9ef 100644 --- a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs +++ b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs @@ -1,18 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; using Emby.Dlna; using Emby.Dlna.PlayTo; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller; using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Serialization; +using Microsoft.Extensions.Logging; +using Moq; using Xunit; namespace Jellyfin.Dlna.Tests { public class ProfileTester { + private DlnaManager GetManager() + { + var xmlSerializer = new Mock(); + var fileSystem = new Mock(); + var appPaths = new Mock(); + var loggerFactory = new Mock(); + var appHost = new Mock(); + + return new DlnaManager(xmlSerializer.Object, fileSystem.Object, appPaths.Object, loggerFactory.Object, appHost.Object); + } + [Fact] public void Test_Profile_Matches() { @@ -46,7 +57,7 @@ namespace Jellyfin.Dlna.Tests } }; - Assert.True(DlnaManager.IsMatch(device.ToDeviceIdentification(), profile.Identification)); + Assert.True(GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification)); var profile2 = new DeviceProfile() { @@ -58,7 +69,7 @@ namespace Jellyfin.Dlna.Tests } }; - Assert.True(DlnaManager.IsMatch(device.ToDeviceIdentification(), profile2.Identification)); + Assert.True(GetManager().IsMatch(device.ToDeviceIdentification(), profile2.Identification)); } [Fact] @@ -90,7 +101,7 @@ namespace Jellyfin.Dlna.Tests } }; - Assert.False(DlnaManager.IsMatch(device.ToDeviceIdentification(), profile.Identification)); + Assert.False(GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification)); } } } From 7848ea148438baf8929e1c2939e6979cb84aad1a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 20 Apr 2021 18:08:19 +0100 Subject: [PATCH 323/402] missed one. --- Emby.Dlna/DlnaManager.cs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index e80f1aaee..7f8dba35f 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -106,28 +106,19 @@ namespace Emby.Dlna throw new ArgumentNullException(nameof(deviceInfo)); } - try + var profile = GetProfiles() + .FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification)); + + if (profile != null) { - var profile = GetProfiles() - .FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification)); - - if (profile != null) - { - _logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name); - } - else - { - LogUnmatchedProfile(deviceInfo); - } - - return profile; + _logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name); } - catch (ArgumentException ex) + else { - _logger.LogError(ex, "Error in profile comparison."); + LogUnmatchedProfile(deviceInfo); } - return null; + return profile; } private void LogUnmatchedProfile(DeviceIdentification profile) From 41246909dcbf71a85140b5354e5f76e33ea4801e Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 20 Apr 2021 18:14:23 +0100 Subject: [PATCH 324/402] fixed merge --- Emby.Dlna/DlnaManager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 8aed4a50f..a554a4d5b 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -173,8 +173,16 @@ namespace Emby.Dlna return false; } - return input.Equals(pattern, StringComparison.OrdinalIgnoreCase) + try + { + return input.Equals(pattern, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + } + catch (ArgumentException ex) + { + _logger.LogError(ex, "Error evaluating regex pattern {Pattern}", pattern); + return false; + } } public DeviceProfile GetProfile(IHeaderDictionary headers) From 522d5a71582fa4b610c75b0029812a9c10619002 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Tue, 20 Apr 2021 18:17:48 +0100 Subject: [PATCH 325/402] Fixed indent --- Emby.Dlna/DlnaManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index a554a4d5b..57a6c2059 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -176,7 +176,7 @@ namespace Emby.Dlna try { return input.Equals(pattern, StringComparison.OrdinalIgnoreCase) - || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } catch (ArgumentException ex) { From 63e9b1ae2d8ea159427fb304d8d8503f7003e085 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 20 Apr 2021 22:59:51 +0200 Subject: [PATCH 326/402] DeepCopy: Throw ArgumentNullException if one of the args is null --- .../Entities/BaseItemExtensions.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs index c65477d39..157ed8332 100644 --- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs +++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs @@ -1,5 +1,7 @@ +#nullable enable #pragma warning disable CS1591 +using System; using System.Linq; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; @@ -64,9 +66,19 @@ namespace MediaBrowser.Controller.Entities /// The source object. /// The destination object. public static void DeepCopy(this T source, TU dest) - where T : BaseItem - where TU : BaseItem + where T : BaseItem + where TU : BaseItem { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (dest == null) + { + throw new ArgumentNullException(nameof(dest)); + } + var destProps = typeof(TU).GetProperties().Where(x => x.CanWrite).ToList(); foreach (var sourceProp in typeof(T).GetProperties()) @@ -99,8 +111,8 @@ namespace MediaBrowser.Controller.Entities /// /// The source object. public static TU DeepCopy(this T source) - where T : BaseItem - where TU : BaseItem, new() + where T : BaseItem + where TU : BaseItem, new() { var dest = new TU(); source.DeepCopy(dest); From 499bac51854cf880beb4add835babf8abd8eb738 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 20 Apr 2021 23:03:36 +0200 Subject: [PATCH 327/402] EncodingHelper: Fix circular dependency --- .../ApplicationHost.cs | 3 +-- .../Controllers/DynamicHlsController.cs | 24 +++++------------ .../Controllers/VideoHlsController.cs | 24 +++++------------ Jellyfin.Api/Controllers/VideosController.cs | 25 +++++------------ Jellyfin.Api/Helpers/AudioHelper.cs | 25 +++++------------ Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 27 ++++++------------- Jellyfin.Api/Helpers/StreamingHelpers.cs | 9 ++----- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 8 +++--- .../MediaEncoding/EncodingHelper.cs | 8 +----- .../Encoder/MediaEncoder.cs | 3 --- 10 files changed, 42 insertions(+), 114 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0512adf10..1ed74210d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -610,9 +610,8 @@ namespace Emby.Server.Implementations // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required ServiceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); - // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required - ServiceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); // TODO: Refactor to eliminate the circular dependencies here so that Lazy isn't required ServiceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index c4e75fe85..b4154b361 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -52,8 +52,6 @@ namespace Jellyfin.Api.Controllers private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; private readonly IFileSystem _fileSystem; - private readonly ISubtitleEncoder _subtitleEncoder; - private readonly IConfiguration _configuration; private readonly IDeviceManager _deviceManager; private readonly TranscodingJobHelper _transcodingJobHelper; private readonly ILogger _logger; @@ -72,12 +70,11 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the class. /// Instance of the interface. /// Instance of . + /// Instance of . public DynamicHlsController( ILibraryManager libraryManager, IUserManager userManager, @@ -87,15 +84,12 @@ namespace Jellyfin.Api.Controllers IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, ILogger logger, - DynamicHlsHelper dynamicHlsHelper) + DynamicHlsHelper dynamicHlsHelper, + EncodingHelper encodingHelper) { - _encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration); - _libraryManager = libraryManager; _userManager = userManager; _dlnaManager = dlnaManager; @@ -104,12 +98,12 @@ namespace Jellyfin.Api.Controllers _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; _fileSystem = fileSystem; - _subtitleEncoder = subtitleEncoder; - _configuration = configuration; _deviceManager = deviceManager; _transcodingJobHelper = transcodingJobHelper; _logger = logger; _dynamicHlsHelper = dynamicHlsHelper; + _encodingHelper = encodingHelper; + _encodingOptions = serverConfigurationManager.GetEncodingOptions(); } @@ -1126,9 +1120,7 @@ namespace Jellyfin.Api.Controllers _libraryManager, _serverConfigurationManager, _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, + _encodingHelper, _dlnaManager, _deviceManager, _transcodingJobHelper, @@ -1211,9 +1203,7 @@ namespace Jellyfin.Api.Controllers _libraryManager, _serverConfigurationManager, _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, + _encodingHelper, _dlnaManager, _deviceManager, _transcodingJobHelper, diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index e95410d02..308334b23 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -48,9 +48,6 @@ namespace Jellyfin.Api.Controllers private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly IFileSystem _fileSystem; - private readonly ISubtitleEncoder _subtitleEncoder; - private readonly IConfiguration _configuration; private readonly IDeviceManager _deviceManager; private readonly TranscodingJobHelper _transcodingJobHelper; private readonly ILogger _logger; @@ -60,9 +57,6 @@ namespace Jellyfin.Api.Controllers /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -72,11 +66,9 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// The singleton. /// Instance of the . + /// Instance of . public VideoHlsController( IMediaEncoder mediaEncoder, - IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, IDlnaManager dlnaManager, IUserManager userManger, IAuthorizationContext authorizationContext, @@ -85,10 +77,9 @@ namespace Jellyfin.Api.Controllers IServerConfigurationManager serverConfigurationManager, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, - ILogger logger) + ILogger logger, + EncodingHelper encodingHelper) { - _encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration); - _dlnaManager = dlnaManager; _authContext = authorizationContext; _userManager = userManger; @@ -96,12 +87,11 @@ namespace Jellyfin.Api.Controllers _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; - _fileSystem = fileSystem; - _subtitleEncoder = subtitleEncoder; - _configuration = configuration; _deviceManager = deviceManager; _transcodingJobHelper = transcodingJobHelper; _logger = logger; + _encodingHelper = encodingHelper; + _encodingOptions = serverConfigurationManager.GetEncodingOptions(); } @@ -285,9 +275,7 @@ namespace Jellyfin.Api.Controllers _libraryManager, _serverConfigurationManager, _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, + _encodingHelper, _dlnaManager, _deviceManager, _transcodingJobHelper, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 699ca5327..8dbb6aaa5 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -49,12 +49,10 @@ namespace Jellyfin.Api.Controllers private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly IFileSystem _fileSystem; - private readonly ISubtitleEncoder _subtitleEncoder; - private readonly IConfiguration _configuration; private readonly IDeviceManager _deviceManager; private readonly TranscodingJobHelper _transcodingJobHelper; private readonly IHttpClientFactory _httpClientFactory; + private readonly EncodingHelper _encodingHelper; private readonly TranscodingJobType _transcodingJobType = TranscodingJobType.Progressive; @@ -69,12 +67,10 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the class. /// Instance of the interface. + /// Instance of . public VideosController( ILibraryManager libraryManager, IUserManager userManager, @@ -84,12 +80,10 @@ namespace Jellyfin.Api.Controllers IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, - IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, - IHttpClientFactory httpClientFactory) + IHttpClientFactory httpClientFactory, + EncodingHelper encodingHelper) { _libraryManager = libraryManager; _userManager = userManager; @@ -99,12 +93,10 @@ namespace Jellyfin.Api.Controllers _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; - _fileSystem = fileSystem; - _subtitleEncoder = subtitleEncoder; - _configuration = configuration; _deviceManager = deviceManager; _transcodingJobHelper = transcodingJobHelper; _httpClientFactory = httpClientFactory; + _encodingHelper = encodingHelper; } /// @@ -444,9 +436,7 @@ namespace Jellyfin.Api.Controllers _libraryManager, _serverConfigurationManager, _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, + _encodingHelper, _dlnaManager, _deviceManager, _transcodingJobHelper, @@ -515,8 +505,7 @@ namespace Jellyfin.Api.Controllers // Need to start ffmpeg (because media can't be returned directly) var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); - var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); - var ffmpegCommandLineArguments = encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, "superfast"); + var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, "superfast"); return await FileStreamResponseHelpers.GetTranscodedFile( state, isHeadRequest, diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs index 9c35d1ec1..cf35ee23a 100644 --- a/Jellyfin.Api/Helpers/AudioHelper.cs +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -32,13 +32,11 @@ namespace Jellyfin.Api.Helpers private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly IFileSystem _fileSystem; - private readonly ISubtitleEncoder _subtitleEncoder; - private readonly IConfiguration _configuration; private readonly IDeviceManager _deviceManager; private readonly TranscodingJobHelper _transcodingJobHelper; private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpContextAccessor _httpContextAccessor; + private readonly EncodingHelper _encodingHelper; /// /// Initializes a new instance of the class. @@ -50,13 +48,11 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of . /// Instance of the interface. /// Instance of the interface. + /// Instance of . public AudioHelper( IDlnaManager dlnaManager, IAuthorizationContext authContext, @@ -65,13 +61,11 @@ namespace Jellyfin.Api.Helpers IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, - IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + EncodingHelper encodingHelper) { _dlnaManager = dlnaManager; _authContext = authContext; @@ -80,13 +74,11 @@ namespace Jellyfin.Api.Helpers _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; - _fileSystem = fileSystem; - _subtitleEncoder = subtitleEncoder; - _configuration = configuration; _deviceManager = deviceManager; _transcodingJobHelper = transcodingJobHelper; _httpClientFactory = httpClientFactory; _httpContextAccessor = httpContextAccessor; + _encodingHelper = encodingHelper; } /// @@ -116,9 +108,7 @@ namespace Jellyfin.Api.Helpers _libraryManager, _serverConfigurationManager, _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, + _encodingHelper, _dlnaManager, _deviceManager, _transcodingJobHelper, @@ -187,8 +177,7 @@ namespace Jellyfin.Api.Helpers // Need to start ffmpeg (because media can't be returned directly) var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); - var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); - var ffmpegCommandLineArguments = encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath); + var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath); return await FileStreamResponseHelpers.GetTranscodedFile( state, isHeadRequest, diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 1bb504ad1..fcada0e77 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -40,14 +40,12 @@ namespace Jellyfin.Api.Helpers private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly IFileSystem _fileSystem; - private readonly ISubtitleEncoder _subtitleEncoder; - private readonly IConfiguration _configuration; private readonly IDeviceManager _deviceManager; private readonly TranscodingJobHelper _transcodingJobHelper; private readonly INetworkManager _networkManager; private readonly ILogger _logger; private readonly IHttpContextAccessor _httpContextAccessor; + private readonly EncodingHelper _encodingHelper; /// /// Initializes a new instance of the class. @@ -59,14 +57,12 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of . /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of . public DynamicHlsHelper( ILibraryManager libraryManager, IUserManager userManager, @@ -75,14 +71,12 @@ namespace Jellyfin.Api.Helpers IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, - IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, INetworkManager networkManager, ILogger logger, - IHttpContextAccessor httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + EncodingHelper encodingHelper) { _libraryManager = libraryManager; _userManager = userManager; @@ -91,14 +85,12 @@ namespace Jellyfin.Api.Helpers _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; - _fileSystem = fileSystem; - _subtitleEncoder = subtitleEncoder; - _configuration = configuration; _deviceManager = deviceManager; _transcodingJobHelper = transcodingJobHelper; _networkManager = networkManager; _logger = logger; _httpContextAccessor = httpContextAccessor; + _encodingHelper = encodingHelper; } /// @@ -144,9 +136,7 @@ namespace Jellyfin.Api.Helpers _libraryManager, _serverConfigurationManager, _mediaEncoder, - _fileSystem, - _subtitleEncoder, - _configuration, + _encodingHelper, _dlnaManager, _deviceManager, _transcodingJobHelper, @@ -227,9 +217,8 @@ namespace Jellyfin.Api.Helpers var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", string.Join(',', requestedVideoProfiles), "main"); sdrVideoUrl += "&AllowVideoStreamCopy=false"; - EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); - var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0; - var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0; + var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0; + var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0; var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate; AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup); diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index e2306aa27..bab901c15 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -41,9 +41,7 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. + /// Instance of . /// Instance of the interface. /// Instance of the interface. /// Initialized . @@ -59,16 +57,13 @@ namespace Jellyfin.Api.Helpers ILibraryManager libraryManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, - IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, + EncodingHelper encodingHelper, IDlnaManager dlnaManager, IDeviceManager deviceManager, TranscodingJobHelper transcodingJobHelper, TranscodingJobType transcodingJobType, CancellationToken cancellationToken) { - EncodingHelper encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration); // Parse the DLNA time seek header if (!streamingRequest.StartTimeTicks.HasValue) { diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 240d132b1..0879cbd18 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -62,8 +62,7 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. + /// Instance of . /// Instance of the interface. public TranscodingJobHelper( ILogger logger, @@ -73,8 +72,7 @@ namespace Jellyfin.Api.Helpers IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager, IAuthorizationContext authorizationContext, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration, + EncodingHelper encodingHelper, ILoggerFactory loggerFactory) { _logger = logger; @@ -84,8 +82,8 @@ namespace Jellyfin.Api.Helpers _serverConfigurationManager = serverConfigurationManager; _sessionManager = sessionManager; _authorizationContext = authorizationContext; + _encodingHelper = encodingHelper; _loggerFactory = loggerFactory; - _encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration); DeleteEncodedMediaCache(); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 1379efacb..2b5364775 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -25,9 +25,7 @@ namespace MediaBrowser.Controller.MediaEncoding private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IMediaEncoder _mediaEncoder; - private readonly IFileSystem _fileSystem; private readonly ISubtitleEncoder _subtitleEncoder; - private readonly IConfiguration _configuration; private static readonly string[] _videoProfiles = new[] { @@ -42,14 +40,10 @@ namespace MediaBrowser.Controller.MediaEncoding public EncodingHelper( IMediaEncoder mediaEncoder, - IFileSystem fileSystem, - ISubtitleEncoder subtitleEncoder, - IConfiguration configuration) + ISubtitleEncoder subtitleEncoder) { _mediaEncoder = mediaEncoder; - _fileSystem = fileSystem; _subtitleEncoder = subtitleEncoder; - _configuration = configuration; } public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 36bf77c84..62c0c0bb1 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -52,7 +52,6 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly IServerConfigurationManager _configurationManager; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; - private readonly Lazy _encodingHelperFactory; private readonly string _startupOptionFFmpegPath; private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2); @@ -76,14 +75,12 @@ namespace MediaBrowser.MediaEncoding.Encoder IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, - Lazy encodingHelperFactory, IConfiguration config) { _logger = logger; _configurationManager = configurationManager; _fileSystem = fileSystem; _localization = localization; - _encodingHelperFactory = encodingHelperFactory; _startupOptionFFmpegPath = config.GetValue(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty; _jsonSerializerOptions = JsonDefaults.Options; } From dcd96909cdade6b8889ae29054af2927a416c0fe Mon Sep 17 00:00:00 2001 From: artiume Date: Tue, 20 Apr 2021 19:28:18 -0400 Subject: [PATCH 328/402] Fix Audiobook Resume https://github.com/jellyfin/jellyfin/issues/5703 --- Emby.Server.Implementations/Library/UserDataManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index e8caea196..78dc95b62 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -251,7 +251,7 @@ namespace Emby.Server.Implementations.Library var minIn = TimeSpan.FromTicks(positionTicks).TotalMinutes; var minOut = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes; - if (minIn > _config.Configuration.MinAudiobookResume) + if (minIn < _config.Configuration.MinAudiobookResume) { // ignore progress during the beginning positionTicks = 0; From 96348ed744d95d0484c34a03b7a218d5153fae65 Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 21 Apr 2021 08:33:29 +0200 Subject: [PATCH 329/402] Add tvrage and imdb ids for episodes --- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index b455e5634..1b6d558a0 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -121,9 +121,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV CommunityRating = Convert.ToSingle(episodeResult.VoteAverage) }; - if (!string.IsNullOrEmpty(episodeResult.ExternalIds?.TvdbId)) + var externalIds = episodeResult.ExternalIds; + if (!string.IsNullOrEmpty(externalIds?.TvdbId)) { - item.SetProviderId(MetadataProvider.Tvdb, episodeResult.ExternalIds.TvdbId); + item.SetProviderId(MetadataProvider.Tvdb, externalIds.TvdbId); + } + + if (!string.IsNullOrWhiteSpace(externalIds?.ImdbId)) + { + item.SetProviderId(MetadataProvider.Imdb, externalIds.ImdbId); + } + + if (!string.IsNullOrEmpty(externalIds?.TvrageId)) + { + item.SetProviderId(MetadataProvider.TvRage, externalIds.TvrageId); } if (episodeResult.Videos?.Results != null) From 2559ceffab778933a6122d2989cfa4d58f699839 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 21 Apr 2021 10:09:05 +0100 Subject: [PATCH 330/402] Update tests/Jellyfin.Dlna.Tests/ProfileTester.cs Co-authored-by: Claus Vium --- tests/Jellyfin.Dlna.Tests/ProfileTester.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs index 3676cc9ef..74868bc0e 100644 --- a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs +++ b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs @@ -11,7 +11,7 @@ using Xunit; namespace Jellyfin.Dlna.Tests { - public class ProfileTester + public class DlnaManagerTests { private DlnaManager GetManager() { From 740e5ec167fc8a147cfaca2904220baee6ccc509 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 21 Apr 2021 10:09:14 +0100 Subject: [PATCH 331/402] Update tests/Jellyfin.Dlna.Tests/ProfileTester.cs Co-authored-by: Claus Vium --- tests/Jellyfin.Dlna.Tests/ProfileTester.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs index 74868bc0e..2f27a0534 100644 --- a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs +++ b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs @@ -73,7 +73,7 @@ namespace Jellyfin.Dlna.Tests } [Fact] - public void Test_Profile_NoMatch() + public void IsMatch_GivenNamesAndManufacturersDoNotMatch_ReturnsFalse() { var device = new DeviceInfo() { From 39eb5da44f8e931aa6a89f9bb3d0785ca5e84465 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 21 Apr 2021 10:09:21 +0100 Subject: [PATCH 332/402] Update tests/Jellyfin.Dlna.Tests/ProfileTester.cs Co-authored-by: Claus Vium --- tests/Jellyfin.Dlna.Tests/ProfileTester.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs index 2f27a0534..004933ca9 100644 --- a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs +++ b/tests/Jellyfin.Dlna.Tests/ProfileTester.cs @@ -25,7 +25,7 @@ namespace Jellyfin.Dlna.Tests } [Fact] - public void Test_Profile_Matches() + public void IsMatch_GivenMatchingName_ReturnsTrue() { var device = new DeviceInfo() { From 53e1b302cc08181c7909719df0cd837fd4b182f4 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 21 Apr 2021 10:18:29 +0100 Subject: [PATCH 333/402] Changes as requested --- Emby.Dlna/DlnaManager.cs | 14 ++++----- .../{ProfileTester.cs => DlnaManagerTests.cs} | 31 ++++++++++++++++--- 2 files changed, 34 insertions(+), 11 deletions(-) rename tests/Jellyfin.Dlna.Tests/{ProfileTester.cs => DlnaManagerTests.cs} (78%) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 57a6c2059..5f2be07cf 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -150,13 +150,13 @@ namespace Emby.Dlna public bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) { return IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName) - && IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer) - && IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl) - && IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription) - && IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName) - && IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber) - && IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl) - && IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber); + && IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer) + && IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl) + && IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription) + && IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName) + && IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber) + && IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl) + && IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber); } private bool IsRegexOrSubstringMatch(string input, string pattern) diff --git a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs b/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs similarity index 78% rename from tests/Jellyfin.Dlna.Tests/ProfileTester.cs rename to tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs index 004933ca9..087d43a77 100644 --- a/tests/Jellyfin.Dlna.Tests/ProfileTester.cs +++ b/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs @@ -46,7 +46,7 @@ namespace Jellyfin.Dlna.Tests ModelDescription = "LG WebOSTV DMRplus", ModelName = "LG TV", ModelNumber = "1.0", - Identification = new DeviceIdentification() + Identification = new () { FriendlyName = "My Device", Manufacturer = "LG Electronics", @@ -69,7 +69,8 @@ namespace Jellyfin.Dlna.Tests } }; - Assert.True(GetManager().IsMatch(device.ToDeviceIdentification(), profile2.Identification)); + var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile2.Identification); + Assert.True(deviceMatch); } [Fact] @@ -90,7 +91,7 @@ namespace Jellyfin.Dlna.Tests ModelDescription = "LG WebOSTV DMRplus", ModelName = "LG TV", ModelNumber = "1.0", - Identification = new DeviceIdentification() + Identification = new () { FriendlyName = "My Device", Manufacturer = "LG Electronics", @@ -101,7 +102,29 @@ namespace Jellyfin.Dlna.Tests } }; - Assert.False(GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification)); + var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification); + + Assert.False(deviceMatch); + } + + [Fact] + public void IsMatch_GivenNamesAndRegExMatch_ReturnsTrue() + { + var device = new DeviceInfo() + { + Name = "My Device" + }; + + var profile = new DeviceProfile() + { + Name = "Test Profile", + FriendlyName = "My .*", + Identification = new () + }; + + var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification); + + Assert.True(deviceMatch); } } } From 20e19ae9b3a89e4c9aac00f2352eb6e3ec52230a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 21 Apr 2021 12:08:02 +0100 Subject: [PATCH 334/402] Moved Assert --- tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs b/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs index 087d43a77..668bd8f87 100644 --- a/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs +++ b/tests/Jellyfin.Dlna.Tests/DlnaManagerTests.cs @@ -57,8 +57,6 @@ namespace Jellyfin.Dlna.Tests } }; - Assert.True(GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification)); - var profile2 = new DeviceProfile() { Name = "Test Profile", @@ -70,7 +68,10 @@ namespace Jellyfin.Dlna.Tests }; var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile2.Identification); + var deviceMatch2 = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification); + Assert.True(deviceMatch); + Assert.True(deviceMatch2); } [Fact] From 005ae80b31c8f354f83390a7b587f058e6792702 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 21 Apr 2021 07:11:14 -0400 Subject: [PATCH 335/402] Update var names --- Emby.Server.Implementations/Library/UserDataManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 78dc95b62..827e3c64b 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -248,15 +248,15 @@ namespace Emby.Server.Implementations.Library } else if (positionTicks > 0 && hasRuntime && item is AudioBook) { - var minIn = TimeSpan.FromTicks(positionTicks).TotalMinutes; - var minOut = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes; + var playbackPositionInMinutes = TimeSpan.FromTicks(positionTicks).TotalMinutes; + var remainingTimeInMinutes = TimeSpan.FromTicks(runtimeTicks - positionTicks).TotalMinutes; - if (minIn < _config.Configuration.MinAudiobookResume) + if (playbackPositionInMinutes < _config.Configuration.MinAudiobookResume) { // ignore progress during the beginning positionTicks = 0; } - else if (minOut < _config.Configuration.MaxAudiobookResume || positionTicks >= runtimeTicks) + else if (remainingTimeInMinutes < _config.Configuration.MaxAudiobookResume || positionTicks >= runtimeTicks) { // mark as completed close to the end positionTicks = 0; From aa992efd312dc4478202d484151fb3b4d46ecbc8 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 21 Apr 2021 16:15:31 +0200 Subject: [PATCH 336/402] Update MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs --- MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index 1b6d558a0..8ab2b19f9 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -127,7 +127,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV item.SetProviderId(MetadataProvider.Tvdb, externalIds.TvdbId); } - if (!string.IsNullOrWhiteSpace(externalIds?.ImdbId)) + if (!string.IsNullOrEmpty(externalIds?.ImdbId)) { item.SetProviderId(MetadataProvider.Imdb, externalIds.ImdbId); } From f46195899e176912676511f277e31fea41d8ab27 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 21 Apr 2021 22:25:08 +0200 Subject: [PATCH 337/402] Improve perf of db save and query --- Jellyfin.Api/Controllers/MoviesController.cs | 9 +++++---- Jellyfin.Api/Controllers/SuggestionsController.cs | 2 +- Jellyfin.Api/Controllers/TvShowsController.cs | 2 +- MediaBrowser.Controller/Entities/TV/Episode.cs | 10 +++++++++- MediaBrowser.Controller/Entities/TV/Season.cs | 11 ++++++++++- MediaBrowser.Controller/Entities/TV/Series.cs | 14 ++++++-------- .../Entities/UserViewBuilder.cs | 10 ++++------ MediaBrowser.Controller/Playlists/Playlist.cs | 4 ++-- 8 files changed, 38 insertions(+), 24 deletions(-) diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 4d788ad7d..d0a2358ae 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers // nameof(LiveTvProgram) }, // IsMovie = true - OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.Random, SortOrder.Descending) }, Limit = 7, ParentId = parentIdGuid, Recursive = true, @@ -110,7 +110,7 @@ namespace Jellyfin.Api.Controllers { IncludeItemTypes = itemTypes.ToArray(), IsMovie = true, - OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) }, Limit = 10, IsFavoriteOrLiked = true, ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id).ToArray(), @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers DtoOptions = dtoOptions }); - var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList(); + var mostRecentMovies = recentlyPlayedMovies.GetRange(0, Math.Min(recentlyPlayedMovies.Count, 6)); // Get recently played directors var recentDirectors = GetDirectors(mostRecentMovies) .ToList(); @@ -191,7 +191,8 @@ namespace Jellyfin.Api.Controllers foreach (var name in names) { - var items = _libraryManager.GetItemList(new InternalItemsQuery(user) + var items = _libraryManager.GetItemList( + new InternalItemsQuery(user) { Person = name, // Account for duplicates by imdb id, since the database doesn't support this yet diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index a55f13e66..a811a29c3 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers var dtoOptions = new DtoOptions().AddClientFields(Request); var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { - OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) }, MediaTypes = mediaType, IncludeItemTypes = type, IsVirtualItem = false, diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index e1c67f830..59400db2a 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -155,7 +155,7 @@ namespace Jellyfin.Api.Controllers var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { IncludeItemTypes = new[] { nameof(Episode) }, - OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(), + OrderBy = new[] { (ItemSortBy.PremiereDate, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) }, MinPremiereDate = minPremiereDate, StartIndex = startIndex, Limit = limit, diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index dc12fbbea..70663ef47 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -99,7 +99,15 @@ namespace MediaBrowser.Controller.Entities.TV take--; } - list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"))); + var newList = seriesUserDataKeys.GetRange(0, take); + var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture); + for (int i = 0; i < take; i++) + { + newList[i] = newList[i] + suffix; + } + + newList.AddRange(list); + list = newList; } return list; diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 93bdd6e70..5b8168d3d 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text.Json.Serialization; using Jellyfin.Data.Entities; @@ -56,7 +57,15 @@ namespace MediaBrowser.Controller.Entities.TV var series = Series; if (series != null) { - list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000"))); + var newList = series.GetUserDataKeys(); + var suffix = (IndexNumber ?? 0).ToString("000", CultureInfo.InvariantCulture); + for (int i = 0; i < newList.Count; i++) + { + newList[i] = newList[i] + suffix; + } + + newList.AddRange(list); + list = newList; } return list; diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index a410c1b66..9f9a2ad50 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -169,14 +169,12 @@ namespace MediaBrowser.Controller.Entities.TV { var list = base.GetUserDataKeys(); - var key = this.GetProviderId(MetadataProvider.Imdb); - if (!string.IsNullOrEmpty(key)) + if (this.TryGetProviderId(MetadataProvider.Imdb, out var key)) { list.Insert(0, key); } - key = this.GetProviderId(MetadataProvider.Tvdb); - if (!string.IsNullOrEmpty(key)) + if (this.TryGetProviderId(MetadataProvider.Tvdb, out key)) { list.Insert(0, key); } @@ -208,7 +206,7 @@ namespace MediaBrowser.Controller.Entities.TV query.AncestorWithPresentationUniqueKey = null; query.SeriesPresentationUniqueKey = seriesKey; query.IncludeItemTypes = new[] { nameof(Season) }; - query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(); + query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }; if (user != null && !user.DisplayMissingEpisodes) { @@ -228,7 +226,7 @@ namespace MediaBrowser.Controller.Entities.TV query.SeriesPresentationUniqueKey = seriesKey; if (query.OrderBy.Count == 0) { - query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(); + query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }; } if (query.IncludeItemTypes.Length == 0) @@ -254,7 +252,7 @@ namespace MediaBrowser.Controller.Entities.TV AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { nameof(Episode), nameof(Season) }, - OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(), + OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, DtoOptions = options }; @@ -365,7 +363,7 @@ namespace MediaBrowser.Controller.Entities.TV AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey, SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null, IncludeItemTypes = new[] { nameof(Episode) }, - OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(), + OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, DtoOptions = options }; if (user != null) diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 4e33a6bbd..78a64d8c9 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -217,8 +217,7 @@ namespace MediaBrowser.Controller.Entities private QueryResult GetMovieLatest(Folder parent, User user, InternalItemsQuery query) { - query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(); - + query.OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) }; query.Recursive = true; query.Parent = parent; query.SetUser(user); @@ -230,7 +229,7 @@ namespace MediaBrowser.Controller.Entities private QueryResult GetMovieResume(Folder parent, User user, InternalItemsQuery query) { - query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(); + query.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) }; query.IsResumable = true; query.Recursive = true; query.Parent = parent; @@ -327,8 +326,7 @@ namespace MediaBrowser.Controller.Entities private QueryResult GetTvLatest(Folder parent, User user, InternalItemsQuery query) { - query.OrderBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(); - + query.OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) }; query.Recursive = true; query.Parent = parent; query.SetUser(user); @@ -356,7 +354,7 @@ namespace MediaBrowser.Controller.Entities private QueryResult GetTvResume(Folder parent, User user, InternalItemsQuery query) { - query.OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(); + query.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) }; query.IsResumable = true; query.Recursive = true; query.Parent = parent; diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index a5b7363fb..c9c168c4c 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -163,7 +163,7 @@ namespace MediaBrowser.Controller.Playlists Recursive = true, IncludeItemTypes = new[] { nameof(Audio) }, GenreIds = new[] { musicGenre.Id }, - OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(), + OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) }, DtoOptions = options }); } @@ -175,7 +175,7 @@ namespace MediaBrowser.Controller.Playlists Recursive = true, IncludeItemTypes = new[] { nameof(Audio) }, ArtistIds = new[] { musicArtist.Id }, - OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(), + OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) }, DtoOptions = options }); } From b0914f9f2c16b538241b74dd22a7bb896fd56b4b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 22 Apr 2021 01:20:55 +0200 Subject: [PATCH 338/402] Remove unused/duplicate services --- Emby.Server.Implementations/ApplicationHost.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 1ed74210d..703f8d20d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -607,9 +607,6 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(); - // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required - ServiceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); - ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); @@ -676,8 +673,6 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); - ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); From b323044139fd7a0b63a717101f7ccb7f03f3f125 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 22 Apr 2021 01:23:24 +0200 Subject: [PATCH 339/402] Reduce string allocations/fs lookups in resolve code --- .../Library/LibraryManager.cs | 1 - .../Library/ResolverHelper.cs | 58 +++++-------------- .../Entities/AggregateFolder.cs | 3 +- .../Entities/CollectionFolder.cs | 1 - .../Library/ItemResolveArgs.cs | 6 +- .../Library/EpisodeResolverTest.cs | 11 +++- 6 files changed, 29 insertions(+), 51 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6a9f4174d..d869c7e39 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -558,7 +558,6 @@ namespace Emby.Server.Implementations.Library var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService) { Parent = parent, - Path = fullPath, FileInfo = fileInfo, CollectionType = collectionType, LibraryOptions = libraryOptions diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index 4e4cac75b..8be80d726 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.IO; using System.Linq; @@ -21,8 +23,8 @@ namespace Emby.Server.Implementations.Library /// The file system. /// The library manager. /// The directory service. - /// Item must have a path - public static void SetInitialItemValues(BaseItem item, Folder parent, IFileSystem fileSystem, ILibraryManager libraryManager, IDirectoryService directoryService) + /// Item must have a path. + public static void SetInitialItemValues(BaseItem item, Folder? parent, IFileSystem fileSystem, ILibraryManager libraryManager, IDirectoryService directoryService) { // This version of the below method has no ItemResolveArgs, so we have to require the path already being set if (string.IsNullOrEmpty(item.Path)) @@ -43,9 +45,9 @@ namespace Emby.Server.Implementations.Library // Make sure DateCreated and DateModified have values var fileInfo = directoryService.GetFile(item.Path); - SetDateCreated(item, fileSystem, fileInfo); + SetDateCreated(item, fileInfo); - EnsureName(item, item.Path, fileInfo); + EnsureName(item, fileInfo); } /// @@ -72,9 +74,9 @@ namespace Emby.Server.Implementations.Library item.Id = libraryManager.GetNewItemId(item.Path, item.GetType()); // Make sure the item has a name - EnsureName(item, item.Path, args.FileInfo); + EnsureName(item, args.FileInfo); - item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || + item.IsLocked = item.Path.Contains("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) || item.GetParents().Any(i => i.IsLocked); // Make sure DateCreated and DateModified have values @@ -84,28 +86,15 @@ namespace Emby.Server.Implementations.Library /// /// Ensures the name. /// - private static void EnsureName(BaseItem item, string fullPath, FileSystemMetadata fileInfo) + private static void EnsureName(BaseItem item, FileSystemMetadata fileInfo) { // If the subclass didn't supply a name, add it here - if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(fullPath)) + if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path)) { - var fileName = fileInfo == null ? Path.GetFileName(fullPath) : fileInfo.Name; - - item.Name = GetDisplayName(fileName, fileInfo != null && fileInfo.IsDirectory); + item.Name = fileInfo.IsDirectory ? fileInfo.Name : Path.GetFileNameWithoutExtension(fileInfo.Name); } } - /// - /// Gets the display name. - /// - /// The path. - /// if set to true [is directory]. - /// System.String. - private static string GetDisplayName(string path, bool isDirectory) - { - return isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); - } - /// /// Ensures DateCreated and DateModified have values. /// @@ -114,21 +103,6 @@ namespace Emby.Server.Implementations.Library /// The args. private static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args) { - if (fileSystem == null) - { - throw new ArgumentNullException(nameof(fileSystem)); - } - - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - if (args == null) - { - throw new ArgumentNullException(nameof(args)); - } - // See if a different path came out of the resolver than what went in if (!fileSystem.AreEqual(args.Path, item.Path)) { @@ -136,7 +110,7 @@ namespace Emby.Server.Implementations.Library if (childData != null) { - SetDateCreated(item, fileSystem, childData); + SetDateCreated(item, childData); } else { @@ -144,17 +118,17 @@ namespace Emby.Server.Implementations.Library if (fileData.Exists) { - SetDateCreated(item, fileSystem, fileData); + SetDateCreated(item, fileData); } } } else { - SetDateCreated(item, fileSystem, args.FileInfo); + SetDateCreated(item, args.FileInfo); } } - private static void SetDateCreated(BaseItem item, IFileSystem fileSystem, FileSystemMetadata info) + private static void SetDateCreated(BaseItem item, FileSystemMetadata? info) { var config = BaseItem.ConfigurationManager.GetMetadataConfiguration(); @@ -163,7 +137,7 @@ namespace Emby.Server.Implementations.Library // directoryService.getFile may return null if (info != null) { - var dateCreated = fileSystem.GetCreationTimeUtc(info); + var dateCreated = info.CreationTimeUtc; if (dateCreated.Equals(DateTime.MinValue)) { diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index 6ebea5f44..6a92200dd 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -120,8 +120,7 @@ namespace MediaBrowser.Controller.Entities var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) { - FileInfo = FileSystem.GetDirectoryInfo(path), - Path = path + FileInfo = FileSystem.GetDirectoryInfo(path) }; // Gather child folder and files diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 76b6d39a9..16a2c77e9 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -271,7 +271,6 @@ namespace MediaBrowser.Controller.Entities var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) { FileInfo = FileSystem.GetDirectoryInfo(path), - Path = path, Parent = GetParent() as Folder, CollectionType = CollectionType }; diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 12a311dc3..df8842237 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -60,10 +60,10 @@ namespace MediaBrowser.Controller.Library public FileSystemMetadata FileInfo { get; set; } /// - /// Gets or sets the path. + /// Gets the path. /// /// The path. - public string Path { get; set; } + public string Path => FileInfo.FullName; /// /// Gets a value indicating whether this instance is directory. @@ -87,7 +87,7 @@ namespace MediaBrowser.Controller.Library return false; } - var parentDir = System.IO.Path.GetDirectoryName(Path) ?? string.Empty; + var parentDir = FileInfo.DirectoryName ?? string.Empty; return parentDir.Length > _appPaths.RootFolderPath.Length && parentDir.StartsWith(_appPaths.RootFolderPath, StringComparison.OrdinalIgnoreCase); diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs index 876519215..c393742eb 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; using Moq; using Xunit; @@ -28,7 +29,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library { Parent = parent, CollectionType = CollectionType.TvShows, - Path = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv" + FileInfo = new FileSystemMetadata() + { + FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv" + } }; Assert.Null(episodeResolver.Resolve(itemResolveArgs)); @@ -48,7 +52,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library { Parent = series, CollectionType = CollectionType.TvShows, - Path = "Extras/Extras S01E01.mkv" + FileInfo = new FileSystemMetadata() + { + FullName = "Extras/Extras S01E01.mkv" + } }; Assert.NotNull(episodeResolver.Resolve(itemResolveArgs)); } From 81209258abeade8614d848f7e7cceeb6bb37a7d7 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 22 Apr 2021 03:11:21 +0200 Subject: [PATCH 340/402] ManagedFileSystem: Rewrite GetValidFilename and more improvements --- .../IO/ManagedFileSystem.cs | 62 ++++++++++--------- .../IO/ManagedFileSystemTests.cs | 10 +++ 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 3893a1577..0d72cb00f 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -2,11 +2,10 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; -using System.Text; +using System.Runtime.InteropServices; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; @@ -24,7 +23,7 @@ namespace Emby.Server.Implementations.IO private readonly List _shortcutHandlers = new List(); private readonly string _tempPath; - private readonly bool _isEnvironmentCaseInsensitive; + private static readonly bool _isEnvironmentCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); public ManagedFileSystem( ILogger logger, @@ -32,8 +31,6 @@ namespace Emby.Server.Implementations.IO { Logger = logger; _tempPath = applicationPaths.TempDirectory; - - _isEnvironmentCaseInsensitive = OperatingSystem.Id == OperatingSystemId.Windows; } public virtual void AddShortcutHandler(IShortcutHandler handler) @@ -55,7 +52,7 @@ namespace Emby.Server.Implementations.IO } var extension = Path.GetExtension(filename); - return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); + return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase)); } /// @@ -72,7 +69,7 @@ namespace Emby.Server.Implementations.IO } var extension = Path.GetExtension(filename); - var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase)); + var handler = _shortcutHandlers.Find(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase)); return handler?.Resolve(filename); } @@ -303,16 +300,38 @@ namespace Emby.Server.Implementations.IO /// The filename. /// System.String. /// The filename is null. - public virtual string GetValidFilename(string filename) + public string GetValidFilename(string filename) { - var builder = new StringBuilder(filename); + return string.Create( + filename.Length, + filename, + (chars, state) => + { + state.AsSpan().CopyTo(chars); - foreach (var c in Path.GetInvalidFileNameChars()) - { - builder = builder.Replace(c, ' '); - } + var invalid = Path.GetInvalidFileNameChars(); - return builder.ToString(); + var first = state.AsSpan().IndexOfAny(invalid); + if (first == -1) + { + // Fast path for clean strings + return; + } + + chars[first++] = ' '; + + var len = chars.Length; + foreach (var c in invalid) + { + for (int i = first; i < len; i++) + { + if (chars[i] == c) + { + chars[i] = ' '; + } + } + } + }); } /// @@ -684,20 +703,5 @@ namespace Emby.Server.Implementations.IO AttributesToSkip = 0 }; } - - private static void RunProcess(string path, string args, string workingDirectory) - { - using (var process = Process.Start(new ProcessStartInfo - { - Arguments = args, - FileName = path, - CreateNoWindow = true, - WorkingDirectory = workingDirectory, - WindowStyle = ProcessWindowStyle.Normal - })) - { - process.WaitForExit(); - } - } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 614a68975..30e6542f9 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -42,6 +42,16 @@ namespace Jellyfin.Server.Implementations.Tests.IO } } + [Theory] + [InlineData("ValidFileName", "ValidFileName")] + [InlineData("AC/DC", "AC DC")] + [InlineData("Invalid\0", "Invalid ")] + [InlineData("AC/DC\0KD/A", "AC DC KD A")] + public void GetValidFilename_ReturnsValidFilename(string filename, string expectedFileName) + { + Assert.Equal(expectedFileName, _sut.GetValidFilename(filename)); + } + [SkippableFact] public void GetFileInfo_DanglingSymlink_ExistsFalse() { From 5b0dc21c644af074095b760b2e42c516ce07c0d6 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 21 Apr 2021 20:46:15 -0600 Subject: [PATCH 341/402] Mark password property as obsolete --- Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs index 393627435..41f7b169e 100644 --- a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs +++ b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs @@ -1,4 +1,6 @@ -namespace Jellyfin.Api.Models.UserDtos +using System; + +namespace Jellyfin.Api.Models.UserDtos { /// /// The authenticate user by name request body. @@ -18,6 +20,7 @@ /// /// Gets or sets the sha1-hashed password. /// + [Obsolete("Send password using pw field")] public string? Password { get; set; } } } From 33327aa1a951a1d0717b39f8f71356f09eb7bd6e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 22 Apr 2021 12:31:47 +0200 Subject: [PATCH 342/402] Improve fast path of ManagedFileSystem.GetValidFilename | Method | Data | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |-------------- |------------:|----------:|----------:|------------:|-------:|------:|------:|----------:| | GetValidFilenameBench | AC/DCKD/A | 52.29 ns | 0.537 ns | 0.448 ns | 52.35 ns | 0.0255 | - | - | 80 B | | GetValidFilenameOldBench | AC/DCKD/A | 86.68 ns | 1.205 ns | 1.127 ns | 86.33 ns | 0.0587 | - | - | 184 B | | GetValidFilenameWinBench | AC/DCKD/A | 448.55 ns | 1.228 ns | 1.088 ns | 448.33 ns | 0.0505 | - | - | 160 B | | GetValidFilenameOldWinBench | AC/DCKD/A | 865.21 ns | 5.734 ns | 5.083 ns | 866.60 ns | 0.0839 | - | - | 264 B | | GetValidFilenameBench | ValidFileName | 16.00 ns | 0.234 ns | 0.207 ns | 16.02 ns | 0.0102 | - | - | 32 B | | GetValidFilenameOldBench | ValidFileName | 100.66 ns | 1.255 ns | 1.174 ns | 101.21 ns | 0.0587 | - | - | 184 B | | GetValidFilenameWinBench | ValidFileName | 116.60 ns | 1.624 ns | 1.519 ns | 116.88 ns | 0.0356 | - | - | 112 B | | GetValidFilenameOldWinBench | ValidFileName | 1,052.66 ns | 18.077 ns | 33.056 ns | 1,037.25 ns | 0.0839 | - | - | 264 B | --- .../IO/ManagedFileSystem.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 0d72cb00f..df973f971 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -302,28 +302,27 @@ namespace Emby.Server.Implementations.IO /// The filename is null. public string GetValidFilename(string filename) { + var invalid = Path.GetInvalidFileNameChars(); + var first = filename.IndexOfAny(invalid); + if (first == -1) + { + // Fast path for clean strings + return filename; + } + return string.Create( filename.Length, - filename, + (filename, invalid, first), (chars, state) => { - state.AsSpan().CopyTo(chars); + state.filename.AsSpan().CopyTo(chars); - var invalid = Path.GetInvalidFileNameChars(); - - var first = state.AsSpan().IndexOfAny(invalid); - if (first == -1) - { - // Fast path for clean strings - return; - } - - chars[first++] = ' '; + chars[state.first++] = ' '; var len = chars.Length; - foreach (var c in invalid) + foreach (var c in state.invalid) { - for (int i = first; i < len; i++) + for (int i = state.first; i < len; i++) { if (chars[i] == c) { From 856819e58f79bdfce3433a9080e673187a4a77c1 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 22 Apr 2021 06:49:42 -0600 Subject: [PATCH 343/402] Don't use obsolete Password property --- Jellyfin.Api/Controllers/UserController.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 3c0d2aca1..b13db4baa 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -177,11 +177,9 @@ namespace Jellyfin.Api.Controllers return StatusCode(StatusCodes.Status403Forbidden, "Only sha1 password is not allowed."); } - // Password should always be null AuthenticateUserByName request = new AuthenticateUserByName { Username = user.Username, - Password = null, Pw = pw }; return await AuthenticateUserByName(request).ConfigureAwait(false); @@ -208,7 +206,6 @@ namespace Jellyfin.Api.Controllers DeviceId = auth.DeviceId, DeviceName = auth.Device, Password = request.Pw, - PasswordSha1 = request.Password, RemoteEndPoint = HttpContext.GetNormalizedRemoteIp().ToString(), Username = request.Username }).ConfigureAwait(false); From 940c30081ef8b9290d9f3c24b5529b2e593f924c Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 22 Apr 2021 06:49:54 -0600 Subject: [PATCH 344/402] Mark PasswordSha1 as obsolete --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- MediaBrowser.Controller/Session/AuthenticationRequest.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 10e28c33a..6f21ec31e 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1485,7 +1485,7 @@ namespace Emby.Server.Implementations.Session user = await _userManager.AuthenticateUser( request.Username, request.Password, - request.PasswordSha1, + null, request.RemoteEndPoint, true).ConfigureAwait(false); } diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs index cc321fd22..8c3ac58f2 100644 --- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs +++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Session public string Password { get; set; } + [Obsolete("Send full password in Password field")] public string PasswordSha1 { get; set; } public string App { get; set; } From a02e37daa009bebc3c5076b0e407dffe41e84c55 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 22 Apr 2021 15:28:24 +0200 Subject: [PATCH 345/402] SqliteItemRepository: remove redundant operations removed: * nameof -> FullName lookup * IndexOf before Replace * Enum.GetNames -> Enum.Parse roundtrip --- .../Data/SqliteItemRepository.cs | 185 +++++++----------- .../Library/LibraryManager.cs | 2 +- .../Persistence/IItemRepository.cs | 3 +- 3 files changed, 68 insertions(+), 122 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 694805ebe..28e59913c 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2116,9 +2116,7 @@ namespace Emby.Server.Implementations.Data || query.IsLiked.HasValue; } - private readonly ItemFields[] _allFields = Enum.GetNames(typeof(ItemFields)) - .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) - .ToArray(); + private readonly ItemFields[] _allFields = Enum.GetValues(); private string[] GetColumnNamesFromField(ItemFields field) { @@ -2721,87 +2719,22 @@ namespace Emby.Server.Implementations.Data private string FixUnicodeChars(string buffer) { - if (buffer.IndexOf('\u2013', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2013', '-'); // en dash - } - - if (buffer.IndexOf('\u2014', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2014', '-'); // em dash - } - - if (buffer.IndexOf('\u2015', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2015', '-'); // horizontal bar - } - - if (buffer.IndexOf('\u2017', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2017', '_'); // double low line - } - - if (buffer.IndexOf('\u2018', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2018', '\''); // left single quotation mark - } - - if (buffer.IndexOf('\u2019', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2019', '\''); // right single quotation mark - } - - if (buffer.IndexOf('\u201a', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark - } - - if (buffer.IndexOf('\u201b', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark - } - - if (buffer.IndexOf('\u201c', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark - } - - if (buffer.IndexOf('\u201d', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark - } - - if (buffer.IndexOf('\u201e', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark - } - - if (buffer.IndexOf('\u2026', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace("\u2026", "...", StringComparison.Ordinal); // horizontal ellipsis - } - - if (buffer.IndexOf('\u2032', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2032', '\''); // prime - } - - if (buffer.IndexOf('\u2033', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u2033', '\"'); // double prime - } - - if (buffer.IndexOf('\u0060', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u0060', '\''); // grave accent - } - - if (buffer.IndexOf('\u00B4', StringComparison.Ordinal) > -1) - { - buffer = buffer.Replace('\u00B4', '\''); // acute accent - } - - return buffer; + buffer = buffer.Replace('\u2013', '-'); // en dash + buffer = buffer.Replace('\u2014', '-'); // em dash + buffer = buffer.Replace('\u2015', '-'); // horizontal bar + buffer = buffer.Replace('\u2017', '_'); // double low line + buffer = buffer.Replace('\u2018', '\''); // left single quotation mark + buffer = buffer.Replace('\u2019', '\''); // right single quotation mark + buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark + buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark + buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark + buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark + buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark + buffer = buffer.Replace("\u2026", "...", StringComparison.Ordinal); // horizontal ellipsis + buffer = buffer.Replace('\u2032', '\''); // prime + buffer = buffer.Replace('\u2033', '\"'); // double prime + buffer = buffer.Replace('\u0060', '\''); // grave accent + return buffer.Replace('\u00B4', '\''); // acute accent } private void AddItem(List items, BaseItem newItem) @@ -3584,11 +3517,11 @@ namespace Emby.Server.Implementations.Data statement?.TryBind("@IsFolder", query.IsFolder); } - var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray(); + var includeTypes = query.IncludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray(); // Only specify excluded types if no included types are specified if (includeTypes.Length == 0) { - var excludeTypes = query.ExcludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray(); + var excludeTypes = query.ExcludeItemTypes.Select(MapIncludeItemTypes).Where(x => x != null).ToArray(); if (excludeTypes.Length == 1) { whereClauses.Add("type<>@type"); @@ -4532,7 +4465,7 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(GetProviderIdClause(query.HasTvdbId.Value, "tvdb")); } - var includedItemByNameTypes = GetItemByNameTypesInQuery(query).SelectMany(MapIncludeItemTypes).ToList(); + var includedItemByNameTypes = GetItemByNameTypesInQuery(query); var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0; var queryTopParentIds = query.TopParentIds; @@ -4790,27 +4723,27 @@ namespace Emby.Server.Implementations.Data if (IsTypeInQuery(nameof(Person), query)) { - list.Add(nameof(Person)); + list.Add(typeof(Person).FullName); } if (IsTypeInQuery(nameof(Genre), query)) { - list.Add(nameof(Genre)); + list.Add(typeof(Genre).FullName); } if (IsTypeInQuery(nameof(MusicGenre), query)) { - list.Add(nameof(MusicGenre)); + list.Add(typeof(MusicGenre).FullName); } if (IsTypeInQuery(nameof(MusicArtist), query)) { - list.Add(nameof(MusicArtist)); + list.Add(typeof(MusicArtist).FullName); } if (IsTypeInQuery(nameof(Studio), query)) { - list.Add(nameof(Studio)); + list.Add(typeof(Studio).FullName); } return list; @@ -4915,15 +4848,10 @@ namespace Emby.Server.Implementations.Data typeof(AggregateFolder) }; - public void UpdateInheritedValues(CancellationToken cancellationToken) - { - UpdateInheritedTags(cancellationToken); - } - - private void UpdateInheritedTags(CancellationToken cancellationToken) + public void UpdateInheritedValues() { string sql = string.Join( - ";", + ';', new string[] { "delete from itemvalues where type = 6", @@ -4946,37 +4874,38 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } } - private static Dictionary GetTypeMapDictionary() + private static Dictionary GetTypeMapDictionary() { - var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var t in _knownTypes) { - dict[t.Name] = new[] { t.FullName }; + dict[t.Name] = t.FullName ; } - dict["Program"] = new[] { typeof(LiveTvProgram).FullName }; - dict["TvChannel"] = new[] { typeof(LiveTvChannel).FullName }; + dict["Program"] = typeof(LiveTvProgram).FullName; + dict["TvChannel"] = typeof(LiveTvChannel).FullName; return dict; } // Not crazy about having this all the way down here, but at least it's in one place - private readonly Dictionary _types = GetTypeMapDictionary(); + private readonly Dictionary _types = GetTypeMapDictionary(); - private string[] MapIncludeItemTypes(string value) + private string MapIncludeItemTypes(string value) { - if (_types.TryGetValue(value, out string[] result)) + if (_types.TryGetValue(value, out string result)) { return result; } if (IsValidType(value)) { - return new[] { value }; + return value; } - return Array.Empty(); + Logger.LogWarning("Unknown item type: {ItemType}", value); + return null; } public void DeleteItem(Guid id) @@ -5279,31 +5208,46 @@ AND Type = @InternalPersonType)"); public List GetStudioNames() { - return GetItemValueNames(new[] { 3 }, new List(), new List()); + return GetItemValueNames(new[] { 3 }, Array.Empty(), Array.Empty()); } public List GetAllArtistNames() { - return GetItemValueNames(new[] { 0, 1 }, new List(), new List()); + return GetItemValueNames(new[] { 0, 1 }, Array.Empty(), Array.Empty()); } public List GetMusicGenreNames() { - return GetItemValueNames(new[] { 2 }, new List { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }, new List()); + return GetItemValueNames( + new[] { 2 }, + new string[] + { + typeof(Audio).FullName, + typeof(MusicVideo).FullName, + typeof(MusicAlbum).FullName, + typeof(MusicArtist).FullName + }, + Array.Empty()); } public List GetGenreNames() { - return GetItemValueNames(new[] { 2 }, new List(), new List { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }); + return GetItemValueNames( + new[] { 2 }, + Array.Empty(), + new string[] + { + typeof(Audio).FullName, + typeof(MusicVideo).FullName, + typeof(MusicAlbum).FullName, + typeof(MusicArtist).FullName + }); } - private List GetItemValueNames(int[] itemValueTypes, List withItemTypes, List excludeItemTypes) + private List GetItemValueNames(int[] itemValueTypes, IReadOnlyList withItemTypes, IReadOnlyList excludeItemTypes) { CheckDisposed(); - withItemTypes = withItemTypes.SelectMany(MapIncludeItemTypes).ToList(); - excludeItemTypes = excludeItemTypes.SelectMany(MapIncludeItemTypes).ToList(); - var now = DateTime.UtcNow; var typeClause = itemValueTypes.Length == 1 ? @@ -5809,7 +5753,10 @@ AND Type = @InternalPersonType)"); var endIndex = Math.Min(people.Count, startIndex + Limit); for (var i = startIndex; i < endIndex; i++) { - insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),", i.ToString(CultureInfo.InvariantCulture)); + insertText.AppendFormat( + CultureInfo.InvariantCulture, + "(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),", + i.ToString(CultureInfo.InvariantCulture)); } // Remove last comma @@ -6261,7 +6208,7 @@ AND Type = @InternalPersonType)"); CheckDisposed(); if (id == Guid.Empty) { - throw new ArgumentException(nameof(id)); + throw new ArgumentException("Guid can't be empty.", nameof(id)); } if (attachments == null) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6a9f4174d..9c5172fb4 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1163,7 +1163,7 @@ namespace Emby.Server.Implementations.Library progress.Report(percent * 100); } - _itemRepository.UpdateInheritedValues(cancellationToken); + _itemRepository.UpdateInheritedValues(); progress.Report(100); } diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 45c6805f0..ed473c749 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -153,8 +153,7 @@ namespace MediaBrowser.Controller.Persistence /// /// Updates the inherited values. /// - /// The cancellation token. - void UpdateInheritedValues(CancellationToken cancellationToken); + void UpdateInheritedValues(); int GetCount(InternalItemsQuery query); From e4691d45f5ffccded535a683a6c2f76a2c8536cb Mon Sep 17 00:00:00 2001 From: Ian Walton Date: Sat, 24 Apr 2021 10:54:42 -0400 Subject: [PATCH 346/402] Leave SyncPlay group on session disconnect. --- .../SyncPlay/SyncPlayManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs index aee959c53..315277985 100644 --- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs +++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs @@ -87,7 +87,7 @@ namespace Emby.Server.Implementations.SyncPlay _sessionManager = sessionManager; _libraryManager = libraryManager; _logger = loggerFactory.CreateLogger(); - _sessionManager.SessionControllerConnected += OnSessionControllerConnected; + _sessionManager.SessionEnded += OnSessionEnded; } /// @@ -352,18 +352,18 @@ namespace Emby.Server.Implementations.SyncPlay return; } - _sessionManager.SessionControllerConnected -= OnSessionControllerConnected; + _sessionManager.SessionEnded -= OnSessionEnded; _disposed = true; } - private void OnSessionControllerConnected(object sender, SessionEventArgs e) + private void OnSessionEnded(object sender, SessionEventArgs e) { var session = e.SessionInfo; if (_sessionToGroupMap.TryGetValue(session.Id, out var group)) { - var request = new JoinGroupRequest(group.GroupId); - JoinGroup(session, request, CancellationToken.None); + var leaveGroupRequest = new LeaveGroupRequest(); + LeaveGroup(session, leaveGroupRequest, CancellationToken.None); } } From 77261a844541efdff96088c047908109d6063133 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 24 Apr 2021 20:22:23 +0200 Subject: [PATCH 347/402] add UpdatePeopleAsync and add people to both tables --- .../Library/LibraryManager.cs | 60 +++++++++++++++++++ .../Library/ILibraryManager.cs | 9 +++ .../Manager/MetadataService.cs | 59 +----------------- 3 files changed, 72 insertions(+), 56 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6a9f4174d..7d030418a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2880,6 +2880,12 @@ namespace Emby.Server.Implementations.Library } public void UpdatePeople(BaseItem item, List people) + { + UpdatePeopleAsync(item, people, CancellationToken.None).GetAwaiter().GetResult(); + } + + /// + public async Task UpdatePeopleAsync(BaseItem item, List people, CancellationToken cancellationToken) { if (!item.SupportsPeople) { @@ -2887,6 +2893,8 @@ namespace Emby.Server.Implementations.Library } _itemRepository.UpdatePeople(item.Id, people); + + await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false); } public async Task ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex) @@ -2990,6 +2998,58 @@ namespace Emby.Server.Implementations.Library } } + private async Task SavePeopleMetadataAsync(IEnumerable people, CancellationToken cancellationToken) + { + var personsToSave = new List(); + + foreach (var person in people) + { + cancellationToken.ThrowIfCancellationRequested(); + + var itemUpdateType = ItemUpdateType.MetadataDownload; + var saveEntity = false; + var personEntity = GetPerson(person.Name); + + // if PresentationUniqueKey is empty it's likely a new item. + if (string.IsNullOrEmpty(personEntity.PresentationUniqueKey)) + { + personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); + saveEntity = true; + } + + foreach (var id in person.ProviderIds) + { + if (!string.Equals(personEntity.GetProviderId(id.Key), id.Value, StringComparison.OrdinalIgnoreCase)) + { + personEntity.SetProviderId(id.Key, id.Value); + saveEntity = true; + } + } + + if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) + { + personEntity.SetImage( + new ItemImageInfo + { + Path = person.ImageUrl, + Type = ImageType.Primary + }, + 0); + + saveEntity = true; + itemUpdateType = ItemUpdateType.ImageUpdate; + } + + if (saveEntity) + { + personsToSave.Add(personEntity); + await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); + } + } + + CreateItems(personsToSave, null, CancellationToken.None); + } + private void StartScanInBackground() { Task.Run(() => diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 80fcf71f3..6d9b568da 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -466,6 +466,15 @@ namespace MediaBrowser.Controller.Library /// The people. void UpdatePeople(BaseItem item, List people); + /// + /// Asynchronously updates the people. + /// + /// The item. + /// The people. + /// The cancellation token. + /// The async task. + Task UpdatePeopleAsync(BaseItem item, List people, CancellationToken cancellationToken); + /// /// Gets the item ids. /// diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index f12586665..6b778a090 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -201,7 +201,7 @@ namespace MediaBrowser.Providers.Manager } // Save to database - await SaveItemAsync(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false); + await SaveItemAsync(metadataResult, updateType, cancellationToken).ConfigureAwait(false); } await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false); @@ -216,71 +216,18 @@ namespace MediaBrowser.Providers.Manager lookupInfo.Year = result.ProductionYear; } - protected async Task SaveItemAsync(MetadataResult result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken) + protected async Task SaveItemAsync(MetadataResult result, ItemUpdateType reason, CancellationToken cancellationToken) { if (result.Item.SupportsPeople && result.People != null) { var baseItem = result.Item; - LibraryManager.UpdatePeople(baseItem, result.People); - await SavePeopleMetadataAsync(result.People, cancellationToken).ConfigureAwait(false); + await LibraryManager.UpdatePeopleAsync(baseItem, result.People, cancellationToken).ConfigureAwait(false); } await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false); } - private async Task SavePeopleMetadataAsync(IEnumerable people, CancellationToken cancellationToken) - { - var personsToSave = new List(); - - foreach (var person in people) - { - cancellationToken.ThrowIfCancellationRequested(); - - var itemUpdateType = ItemUpdateType.MetadataDownload; - var saveEntity = false; - var personEntity = LibraryManager.GetPerson(person.Name); - - // if PresentationUniqueKey is empty it's likely a new item. - if (string.IsNullOrEmpty(personEntity.PresentationUniqueKey)) - { - personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); - saveEntity = true; - } - - foreach (var id in person.ProviderIds) - { - if (!string.Equals(personEntity.GetProviderId(id.Key), id.Value, StringComparison.OrdinalIgnoreCase)) - { - personEntity.SetProviderId(id.Key, id.Value); - saveEntity = true; - } - } - - if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) - { - personEntity.SetImage( - new ItemImageInfo - { - Path = person.ImageUrl, - Type = ImageType.Primary - }, - 0); - - saveEntity = true; - itemUpdateType = ItemUpdateType.ImageUpdate; - } - - if (saveEntity) - { - personsToSave.Add(personEntity); - await LibraryManager.RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); - } - } - - LibraryManager.CreateItems(personsToSave, null, CancellationToken.None); - } - protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { item.AfterMetadataRefresh(); From eee3b385daae376efb6f05af19692bc8b64e9c96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 12:00:42 +0000 Subject: [PATCH 348/402] Bump AutoFixture from 4.16.0 to 4.17.0 Bumps [AutoFixture](https://github.com/AutoFixture/AutoFixture) from 4.16.0 to 4.17.0. - [Release notes](https://github.com/AutoFixture/AutoFixture/releases) - [Commits](https://github.com/AutoFixture/AutoFixture/compare/v4.16.0...v4.17.0) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 050d4c040..0071cda6e 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 486899f4f..7a4ab9b26 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -22,7 +22,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 0de92249a..8646b60b1 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 9e60dbcd9..64383a2d9 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,7 +10,7 @@ - + From d5037a8988df1e4790b45ed55696f7afa7f43b5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 12:00:48 +0000 Subject: [PATCH 349/402] Bump Swashbuckle.AspNetCore from 6.0.7 to 6.1.3 Bumps [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) from 6.0.7 to 6.1.3. - [Release notes](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases) - [Commits](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/compare/v6.0.7...v6.1.3) Signed-off-by: dependabot[bot] --- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index d6dc5a2dc..01341d8b1 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + From 24a05bc9f83236a2e52b5c754c2a775ac1e6c1c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 12:00:57 +0000 Subject: [PATCH 350/402] Bump sharpcompress from 0.28.1 to 0.28.2 Bumps [sharpcompress](https://github.com/adamhathcock/sharpcompress) from 0.28.1 to 0.28.2. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.28.1...0.28.2) Signed-off-by: dependabot[bot] --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 9248053f5..4ee23127e 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -30,7 +30,7 @@ - + From e409d89138b21daef34dd64afb4d6434956b56fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 12:03:24 +0000 Subject: [PATCH 351/402] Bump alex-page/github-project-automation-plus from v0.5.1 to v0.6.0 Bumps [alex-page/github-project-automation-plus](https://github.com/alex-page/github-project-automation-plus) from v0.5.1 to v0.6.0. - [Release notes](https://github.com/alex-page/github-project-automation-plus/releases) - [Commits](https://github.com/alex-page/github-project-automation-plus/compare/v0.5.1...4230e39aec629f1b622666350cdbdf29ff149aca) Signed-off-by: dependabot[bot] --- .github/workflows/automation.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml index db34693cc..d5d308185 100644 --- a/.github/workflows/automation.yml +++ b/.github/workflows/automation.yml @@ -16,7 +16,7 @@ jobs: label: stable backport - name: Remove from 'Current Release' project - uses: alex-page/github-project-automation-plus@v0.5.1 + uses: alex-page/github-project-automation-plus@v0.6.0 if: (github.event.pull_request || github.event.issue.pull_request) && !steps.checkLabel.outputs.hasLabel continue-on-error: true with: @@ -25,7 +25,7 @@ jobs: repo-token: ${{ secrets.GH_TOKEN }} - name: Add to 'Release Next' project - uses: alex-page/github-project-automation-plus@v0.5.1 + uses: alex-page/github-project-automation-plus@v0.6.0 if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened' continue-on-error: true with: @@ -34,7 +34,7 @@ jobs: repo-token: ${{ secrets.GH_TOKEN }} - name: Add to 'Current Release' project - uses: alex-page/github-project-automation-plus@v0.5.1 + uses: alex-page/github-project-automation-plus@v0.6.0 if: (github.event.pull_request || github.event.issue.pull_request) && steps.checkLabel.outputs.hasLabel continue-on-error: true with: @@ -48,7 +48,7 @@ jobs: run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)" - name: Move issue to needs triage - uses: alex-page/github-project-automation-plus@v0.5.1 + uses: alex-page/github-project-automation-plus@v0.6.0 if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1 continue-on-error: true with: @@ -57,7 +57,7 @@ jobs: repo-token: ${{ secrets.GH_TOKEN }} - name: Add issue to triage project - uses: alex-page/github-project-automation-plus@v0.5.1 + uses: alex-page/github-project-automation-plus@v0.6.0 if: github.event.issue.pull_request == '' && github.event.action == 'opened' continue-on-error: true with: From 47c54166e14a89a6895f6ceda3ab48591c097fad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 14:43:14 +0000 Subject: [PATCH 352/402] Bump prometheus-net.DotNetRuntime from 3.4.1 to 4.0.0 Bumps [prometheus-net.DotNetRuntime](https://github.com/djluck/prometheus-net.DotNetRuntime) from 3.4.1 to 4.0.0. - [Release notes](https://github.com/djluck/prometheus-net.DotNetRuntime/releases) - [Commits](https://github.com/djluck/prometheus-net.DotNetRuntime/compare/3.4.1...4.0.0) Signed-off-by: dependabot[bot] --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 4ee23127e..adbfe52c4 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + From a3cb8dbc9de4b8aa9c5bb4f6c5e0bd1ee53f60a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 21:47:14 +0000 Subject: [PATCH 353/402] Bump Swashbuckle.AspNetCore.ReDoc from 6.0.7 to 6.1.3 Bumps [Swashbuckle.AspNetCore.ReDoc](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) from 6.0.7 to 6.1.3. - [Release notes](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases) - [Commits](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/compare/v6.0.7...v6.1.3) Signed-off-by: dependabot[bot] --- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 01341d8b1..3868882e5 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -18,7 +18,7 @@ - + From c1563d9303da386fb37c78cfa4976917fb649b5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Apr 2021 13:35:13 +0000 Subject: [PATCH 354/402] Bump AutoFixture.Xunit2 from 4.16.0 to 4.17.0 Bumps [AutoFixture.Xunit2](https://github.com/AutoFixture/AutoFixture) from 4.16.0 to 4.17.0. - [Release notes](https://github.com/AutoFixture/AutoFixture/releases) - [Commits](https://github.com/AutoFixture/AutoFixture/compare/v4.16.0...v4.17.0) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 0071cda6e..9a5ffaa55 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 8646b60b1..81878eafa 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 64383a2d9..f25998252 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -12,7 +12,7 @@ - + From 34313ef2165b4889454eaac92b563fe1998854c0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 29 Apr 2021 16:23:29 +0200 Subject: [PATCH 355/402] SqliteItemRepository: Parse ChannelId directly from utf-8 data --- .../Data/SqliteItemRepository.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 694805ebe..8d220c807 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Buffers.Text; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -1311,7 +1312,14 @@ namespace Emby.Server.Implementations.Data if (!reader.IsDBNull(index)) { - item.ChannelId = new Guid(reader.GetString(index)); + if (!Utf8Parser.TryParse(reader[index].ToBlob(), out Guid value, out _, standardFormat: 'N')) + { + var str = reader.GetString(index); + Logger.LogWarning("{ChannelId} isn't in the expected format", str); + value = new Guid(str); + } + + item.ChannelId = value; } index++; From bdab8d1edbbad79ccecd4e0325c090ebf6ded2bc Mon Sep 17 00:00:00 2001 From: Nathan Mascitelli Date: Thu, 29 Apr 2021 22:35:57 -0400 Subject: [PATCH 356/402] Add ResumeBook section --- Jellyfin.Data/Enums/HomeSectionType.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Data/Enums/HomeSectionType.cs b/Jellyfin.Data/Enums/HomeSectionType.cs index e597c9431..9bcd097dc 100644 --- a/Jellyfin.Data/Enums/HomeSectionType.cs +++ b/Jellyfin.Data/Enums/HomeSectionType.cs @@ -48,6 +48,11 @@ /// /// Live TV. /// - LiveTv = 8 + LiveTv = 8, + + /// + /// Continue Reading. + /// + ResumeBook = 9 } } From 182117d0a7c60a00e03f69bc8cd237ddd3e73f34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Apr 2021 08:47:38 +0000 Subject: [PATCH 357/402] Bump AutoFixture.AutoMoq from 4.16.0 to 4.17.0 Bumps [AutoFixture.AutoMoq](https://github.com/AutoFixture/AutoFixture) from 4.16.0 to 4.17.0. - [Release notes](https://github.com/AutoFixture/AutoFixture/releases) - [Commits](https://github.com/AutoFixture/AutoFixture/compare/v4.16.0...v4.17.0) Signed-off-by: dependabot[bot] --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 9a5ffaa55..397b863b7 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 7a4ab9b26..27713d58a 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -23,7 +23,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 81878eafa..c1d871126 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index f25998252..72e40ebcb 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -11,7 +11,7 @@ - + From e90fbe90f9dc9603b6d9e07cc2c8ee096062c57b Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Fri, 30 Apr 2021 15:07:27 +0200 Subject: [PATCH 358/402] Remove extraneous 'stream' parameter The argument isn't passed to the method but causes the API generator to include an unnecessary parameter. Also fixes some typos in the documentation comments. --- Jellyfin.Api/Controllers/VideosController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 8dbb6aaa5..e544d001e 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -527,7 +527,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The dlna device profile id to utilize. /// The play session id. /// The segment container. - /// The segment lenght. + /// The segment length. /// The minimum number of segments. /// The media version id, if playing an alternate version. /// The device id of the client requesting. Used to stop encoding processes when needed. @@ -556,7 +556,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The maximum video bit depth. /// Optional. Whether to require avc. /// Optional. Whether to deinterlace the video. - /// Optional. Whether to require a non anamporphic stream. + /// Optional. Whether to require a non anamorphic stream. /// Optional. The maximum number of audio channels to transcode. /// Optional. The limit of how many cpu cores to use. /// The live stream id. @@ -570,8 +570,8 @@ namespace Jellyfin.Api.Controllers /// Optional. The streaming options. /// Video stream returned. /// A containing the audio file. - [HttpGet("{itemId}/{stream=stream}.{container}")] - [HttpHead("{itemId}/{stream=stream}.{container}", Name = "HeadVideoStreamByContainer")] + [HttpGet("{itemId}/stream.{container}")] + [HttpHead("{itemId}/stream.{container}", Name = "HeadVideoStreamByContainer")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesVideoFile] public Task GetVideoStreamByContainer( From 608cba817c54df60959079719bafca4d7d54269a Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 30 Apr 2021 15:09:36 +0200 Subject: [PATCH 359/402] Reduce some allocations with the magic of spans etc. --- .../Data/SqliteItemRepository.cs | 155 ++++++++++++------ .../Library/MediaSourceManager.cs | 13 +- .../LiveTv/EmbyTV/EmbyTV.cs | 14 +- .../Localization/LocalizationManager.cs | 3 +- .../QuickConnect/QuickConnectManager.cs | 15 +- .../SyncPlay/SyncPlayManager.cs | 13 +- .../Extensions/SplitLinesStringExtensions.cs | 102 ++++++++++++ MediaBrowser.Controller/Entities/Folder.cs | 17 +- .../Entities/ProviderIdsExtensions.cs | 30 ++++ 9 files changed, 267 insertions(+), 95 deletions(-) create mode 100644 MediaBrowser.Common/Extensions/SplitLinesStringExtensions.cs diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 28e59913c..bd5a3e30b 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1007,15 +1007,12 @@ namespace Emby.Server.Implementations.Data return; } - var parts = value.Split('|', StringSplitOptions.RemoveEmptyEntries); - - foreach (var part in parts) + foreach (var part in value.SpanSplit('|')) { - var idParts = part.Split('='); - - if (idParts.Length == 2) + var providerDelimiterIndex = part.IndexOf('='); + if (providerDelimiterIndex != -1 && providerDelimiterIndex == part.LastIndexOf('=')) { - item.SetProviderId(idParts[0], idParts[1]); + item.SetProviderId(part.Slice(0, providerDelimiterIndex), part.Slice(providerDelimiterIndex + 1)); } } } @@ -1057,9 +1054,8 @@ namespace Emby.Server.Implementations.Data return; } - var parts = value.Split('|' , StringSplitOptions.RemoveEmptyEntries); var list = new List(); - foreach (var part in parts) + foreach (var part in value.SpanSplit('|')) { var image = ItemImageInfoFromValueString(part); @@ -1094,41 +1090,89 @@ namespace Emby.Server.Implementations.Data .Append(hash.Replace('*', '/').Replace('|', '\\')); } - public ItemImageInfo ItemImageInfoFromValueString(string value) + private ItemImageInfo ItemImageInfoFromValueString(ReadOnlySpan value) { - var parts = value.Split('*', StringSplitOptions.None); - - if (parts.Length < 3) + var nextSegment = value.IndexOf('*'); + if (nextSegment == -1) { return null; } - var image = new ItemImageInfo(); + ReadOnlySpan path = value[..nextSegment]; + value = value[(nextSegment + 1)..]; + nextSegment = value.IndexOf('*'); + if (nextSegment == -1) + { + return null; + } - image.Path = RestorePath(parts[0]); + ReadOnlySpan dateModified = value[..nextSegment]; + value = value[(nextSegment + 1)..]; + nextSegment = value.IndexOf('*'); + if (nextSegment == -1) + { + return null; + } - if (long.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)) + ReadOnlySpan imageType = value[..nextSegment]; + + var image = new ItemImageInfo + { + Path = RestorePath(path.ToString()) + }; + + if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)) { image.DateModified = new DateTime(ticks, DateTimeKind.Utc); } - if (Enum.TryParse(parts[2], true, out ImageType type)) + if (Enum.TryParse(imageType.ToString(), true, out ImageType type)) { image.Type = type; } - if (parts.Length >= 5) + // Optional parameters: width*height*blurhash + if (nextSegment + 1 < value.Length - 1) { - if (int.TryParse(parts[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var width) - && int.TryParse(parts[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var height)) + value = value[(nextSegment + 1)..]; + nextSegment = value.IndexOf('*'); + ReadOnlySpan widthSpan = value[..nextSegment]; + + value = value[(nextSegment + 1)..]; + nextSegment = value.IndexOf('*'); + if (nextSegment == -1) + { + return image; + } + + ReadOnlySpan heightSpan = value[..nextSegment]; + + if (int.TryParse(widthSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var width) + && int.TryParse(heightSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var height)) { image.Width = width; image.Height = height; } - if (parts.Length >= 6) + nextSegment += 1; + if (nextSegment < value.Length - 1) { - image.BlurHash = parts[5].Replace('/', '*').Replace('\\', '|'); + value = value[nextSegment..]; + var length = value.Length; + + Span blurHashSpan = stackalloc char[length]; + for (int i = 0; i < length; i++) + { + var c = value[i]; + blurHashSpan[i] = c switch + { + '/' => '*', + '\\' => '|', + _ => c + }; + } + + image.BlurHash = new string(blurHashSpan); } } @@ -2118,27 +2162,6 @@ namespace Emby.Server.Implementations.Data private readonly ItemFields[] _allFields = Enum.GetValues(); - private string[] GetColumnNamesFromField(ItemFields field) - { - switch (field) - { - case ItemFields.Settings: - return new[] { "IsLocked", "PreferredMetadataCountryCode", "PreferredMetadataLanguage", "LockedFields" }; - case ItemFields.ServiceName: - return new[] { "ExternalServiceId" }; - case ItemFields.SortName: - return new[] { "ForcedSortName" }; - case ItemFields.Taglines: - return new[] { "Tagline" }; - case ItemFields.Tags: - return new[] { "Tags" }; - case ItemFields.IsHD: - return Array.Empty(); - default: - return new[] { field.ToString() }; - } - } - private bool HasField(InternalItemsQuery query, ItemFields name) { switch (name) @@ -2327,9 +2350,32 @@ namespace Emby.Server.Implementations.Data { if (!HasField(query, field)) { - foreach (var fieldToRemove in GetColumnNamesFromField(field)) + switch (field) { - list.Remove(fieldToRemove); + case ItemFields.Settings: + list.Remove("IsLocked"); + list.Remove("PreferredMetadataCountryCode"); + list.Remove("PreferredMetadataLanguage"); + list.Remove("LockedFields"); + break; + case ItemFields.ServiceName: + list.Remove("ExternalServiceId"); + break; + case ItemFields.SortName: + list.Remove("ForcedSortName"); + break; + case ItemFields.Taglines: + list.Remove("Tagline"); + break; + case ItemFields.Tags: + list.Remove("Tags"); + break; + case ItemFields.IsHD: + // do nothing + break; + default: + list.Remove(field.ToString()); + break; } } } @@ -2575,10 +2621,21 @@ namespace Emby.Server.Implementations.Data query.Limit = query.Limit.Value + 4; } - var commandText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" })) - + GetFromText() - + GetJoinUserDataText(query); + var commandText = "select "; + if (EnableGroupByPresentationUniqueKey(query)) + { + commandText += "count (distinct PresentationUniqueKey)"; + } + else if (query.GroupBySeriesPresentationUniqueKey) + { + commandText += "count (distinct SeriesPresentationUniqueKey)"; + } + else + { + commandText += "count (guid)"; + } + + commandText += GetFromText() + GetJoinUserDataText(query); var whereClauses = GetWhereClauses(query, null); if (whereClauses.Count != 0) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index d0b85f07d..85d6d3043 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -590,18 +590,9 @@ namespace Emby.Server.Implementations.Library public Task GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken) { - var info = _openStreams.Values.FirstOrDefault(i => - { - var liveStream = i as ILiveStream; - if (liveStream != null) - { - return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase); - } + var info = _openStreams.FirstOrDefault(i => i.Value != null && string.Equals(i.Value.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase)); - return false; - }); - - return Task.FromResult(info as IDirectStreamProvider); + return Task.FromResult(info.Value as IDirectStreamProvider); } public async Task OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index c9d9cc49a..665fbfa0f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -801,22 +801,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public ActiveRecordingInfo GetActiveRecordingInfo(string path) { - if (string.IsNullOrWhiteSpace(path)) + if (string.IsNullOrWhiteSpace(path) || _activeRecordings.IsEmpty) { return null; } - foreach (var recording in _activeRecordings.Values) + foreach (var (_, recordingInfo) in _activeRecordings) { - if (string.Equals(recording.Path, path, StringComparison.Ordinal) && !recording.CancellationTokenSource.IsCancellationRequested) + if (string.Equals(recordingInfo.Path, path, StringComparison.Ordinal) && !recordingInfo.CancellationTokenSource.IsCancellationRequested) { - var timer = recording.Timer; + var timer = recordingInfo.Timer; if (timer.Status != RecordingStatus.InProgress) { return null; } - return recording; + return recordingInfo; } } @@ -1621,9 +1621,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } return _activeRecordings - .Values - .ToList() - .Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase)); + .Any(i => string.Equals(i.Value.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Value.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase)); } private IRecorder GetRecorder(MediaSourceInfo mediaSource) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 2fdc2b4d9..46858b4fb 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -315,10 +315,9 @@ namespace Emby.Server.Implementations.Localization } const string Prefix = "Core"; - var key = Prefix + culture; return _dictionaries.GetOrAdd( - key, + culture, f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); } diff --git a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs index 22739a008..0259dc436 100644 --- a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs +++ b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs @@ -257,20 +257,17 @@ namespace Emby.Server.Implementations.QuickConnect } // Expire stale connection requests - var code = string.Empty; - var values = _currentRequests.Values.ToList(); - - for (int i = 0; i < values.Count; i++) + foreach (var (_, currentRequest) in _currentRequests) { - var added = values[i].DateAdded ?? DateTime.UnixEpoch; - if (DateTime.UtcNow > added.AddMinutes(Timeout) || expireAll) + var added = currentRequest.DateAdded ?? DateTime.UnixEpoch; + if (expireAll || DateTime.UtcNow > added.AddMinutes(Timeout)) { - code = values[i].Code; - _logger.LogDebug("Removing expired request {code}", code); + var code = currentRequest.Code; + _logger.LogDebug("Removing expired request {Code}", code); if (!_currentRequests.TryRemove(code, out _)) { - _logger.LogWarning("Request {code} already expired", code); + _logger.LogWarning("Request {Code} already expired", code); } } } diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs index 315277985..72c0a838e 100644 --- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs +++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs @@ -269,14 +269,17 @@ namespace Emby.Server.Implementations.SyncPlay var user = _userManager.GetUserById(session.UserId); List list = new List(); - foreach (var group in _groups.Values) + lock (_groupsLock) { - // Locking required as group is not thread-safe. - lock (group) + foreach (var (_, group) in _groups) { - if (group.HasAccessToPlayQueue(user)) + // Locking required as group is not thread-safe. + lock (group) { - list.Add(group.GetInfo()); + if (group.HasAccessToPlayQueue(user)) + { + list.Add(group.GetInfo()); + } } } } diff --git a/MediaBrowser.Common/Extensions/SplitLinesStringExtensions.cs b/MediaBrowser.Common/Extensions/SplitLinesStringExtensions.cs new file mode 100644 index 000000000..5332aba9f --- /dev/null +++ b/MediaBrowser.Common/Extensions/SplitLinesStringExtensions.cs @@ -0,0 +1,102 @@ +/* +MIT License + +Copyright (c) 2019 Gérald Barré + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ +#nullable enable +#pragma warning disable CS1591 +#pragma warning disable CA1034 +using System; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +namespace MediaBrowser.Common.Extensions +{ + /// + /// Extension class for splitting lines without unnecessary allocations. + /// + public static class SplitLinesStringExtensions + { + /// + /// Creates a new line split enumerator. + /// + /// The string to split. + /// The separator to split on. + /// The enumerator struct. + [Pure] + public static LineSplitEnumerator SpanSplit(this string str, char separator) => new (str.AsSpan(), separator); + + /// + /// Creates a new line split enumerator. + /// + /// The span to split. + /// The separator to split on. + /// The enumerator struct. + [Pure] + public static LineSplitEnumerator Split(this ReadOnlySpan str, char separator) => new (str, separator); + + [StructLayout(LayoutKind.Auto)] + public ref struct LineSplitEnumerator + { + private readonly char _separator; + private ReadOnlySpan _str; + + public LineSplitEnumerator(ReadOnlySpan str, char separator) + { + _str = str; + _separator = separator; + Current = default; + } + + public ReadOnlySpan Current { get; private set; } + + public readonly LineSplitEnumerator GetEnumerator() => this; + + public bool MoveNext() + { + if (_str.Length == 0) + { + return false; + } + + var span = _str; + var index = span.IndexOf(_separator); + if (index == -1) + { + _str = ReadOnlySpan.Empty; + Current = span; + return true; + } + + if (index < span.Length - 1 && span[index] == _separator) + { + Current = span.Slice(0, index); + _str = span[(index + 1)..]; + return true; + } + + Current = span.Slice(0, index); + _str = span[(index + 1)..]; + return true; + } + } + } +} diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index d45f8758c..d74e6f9d8 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1768,20 +1768,15 @@ namespace MediaBrowser.Controller.Entities { EnableImages = false } - }); + }).TotalRecordCount; - double unplayedCount = unplayedQueryResult.TotalRecordCount; + dto.UnplayedItemCount = unplayedQueryResult; - dto.UnplayedItemCount = unplayedQueryResult.TotalRecordCount; - - if (itemDto != null && itemDto.RecursiveItemCount.HasValue) + if (itemDto?.RecursiveItemCount > 0) { - if (itemDto.RecursiveItemCount.Value > 0) - { - var unplayedPercentage = (unplayedCount / itemDto.RecursiveItemCount.Value) * 100; - dto.PlayedPercentage = 100 - unplayedPercentage; - dto.Played = dto.PlayedPercentage.Value >= 100; - } + var unplayedPercentage = ((double)unplayedQueryResult / itemDto.RecursiveItemCount.Value) * 100; + dto.PlayedPercentage = 100 - unplayedPercentage; + dto.Played = dto.PlayedPercentage.Value >= 100; } else { diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 09d14dc6a..bc14da7f2 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -132,6 +132,36 @@ namespace MediaBrowser.Model.Entities } } + /// + /// Sets a provider id. + /// + /// The instance. + /// The name. + /// The value. + public static void SetProviderId(this IHasProviderIds instance, ReadOnlySpan name, ReadOnlySpan value) + { + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + // If it's null remove the key from the dictionary + if (value.IsEmpty) + { + instance.ProviderIds?.Remove(name.ToString()); + } + else + { + // Ensure it exists + if (instance.ProviderIds == null) + { + instance.ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + instance.ProviderIds[name.ToString()] = value.ToString(); + } + } + /// /// Sets a provider id. /// From a6726730fc181599db0ba68545adce9b88c11f7b Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 30 Apr 2021 15:11:16 +0200 Subject: [PATCH 360/402] revert the last bits of the getcount experiment --- .../Data/SqliteItemRepository.cs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index bd5a3e30b..7cee309d0 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2621,21 +2621,10 @@ namespace Emby.Server.Implementations.Data query.Limit = query.Limit.Value + 4; } - var commandText = "select "; - if (EnableGroupByPresentationUniqueKey(query)) - { - commandText += "count (distinct PresentationUniqueKey)"; - } - else if (query.GroupBySeriesPresentationUniqueKey) - { - commandText += "count (distinct SeriesPresentationUniqueKey)"; - } - else - { - commandText += "count (guid)"; - } - - commandText += GetFromText() + GetJoinUserDataText(query); + var commandText = "select " + + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" })) + + GetFromText() + + GetJoinUserDataText(query); var whereClauses = GetWhereClauses(query, null); if (whereClauses.Count != 0) From ba2e346d1246cc51e186a64d38a9b7385492ca4f Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 30 Apr 2021 15:27:07 +0200 Subject: [PATCH 361/402] prevent cancellationtoken leakage --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 4 +++- Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs | 3 ++- Jellyfin.Api/Helpers/ProgressiveFileCopier.cs | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 1dcc78687..c32ca2fb6 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -661,7 +661,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _modelCache.Clear(); } - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(discoveryDurationMs).Token, cancellationToken).Token; + using var timedCancellationToken = new CancellationTokenSource(discoveryDurationMs); + using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timedCancellationToken.Token, cancellationToken); + cancellationToken = linkedCancellationTokenSource.Token; var list = new List(); // Create udp broadcast discovery message diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 78e62ff0a..f8baf55da 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -150,7 +150,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken) { - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token; + using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token); + cancellationToken = linkedCancellationTokenSource.Token; // use non-async filestream on windows along with read due to https://github.com/dotnet/corefx/issues/6039 var allowAsync = Environment.OSVersion.Platform != PlatformID.Win32NT; diff --git a/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs b/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs index 8bddf00d5..963e17724 100644 --- a/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs +++ b/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs @@ -71,7 +71,8 @@ namespace Jellyfin.Api.Helpers /// A . public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) { - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken).Token; + using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken); + cancellationToken = linkedCancellationTokenSource.Token; try { From 716cbb06958da987ab917c1f6408396d4ace7a0c Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 30 Apr 2021 23:35:29 +0200 Subject: [PATCH 362/402] remove span based setproviderid --- .../Data/SqliteItemRepository.cs | 2 +- .../Entities/ProviderIdsExtensions.cs | 30 ------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 7cee309d0..e78ebbc50 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1012,7 +1012,7 @@ namespace Emby.Server.Implementations.Data var providerDelimiterIndex = part.IndexOf('='); if (providerDelimiterIndex != -1 && providerDelimiterIndex == part.LastIndexOf('=')) { - item.SetProviderId(part.Slice(0, providerDelimiterIndex), part.Slice(providerDelimiterIndex + 1)); + item.SetProviderId(part.Slice(0, providerDelimiterIndex).ToString(), part.Slice(providerDelimiterIndex + 1).ToString()); } } } diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index bc14da7f2..09d14dc6a 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -132,36 +132,6 @@ namespace MediaBrowser.Model.Entities } } - /// - /// Sets a provider id. - /// - /// The instance. - /// The name. - /// The value. - public static void SetProviderId(this IHasProviderIds instance, ReadOnlySpan name, ReadOnlySpan value) - { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } - - // If it's null remove the key from the dictionary - if (value.IsEmpty) - { - instance.ProviderIds?.Remove(name.ToString()); - } - else - { - // Ensure it exists - if (instance.ProviderIds == null) - { - instance.ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - instance.ProviderIds[name.ToString()] = value.ToString(); - } - } - /// /// Sets a provider id. /// From 70771fdcd60ec5d8a9f13713662778c7e57d0633 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sat, 1 May 2021 13:06:10 +0200 Subject: [PATCH 363/402] Nullability handling for device profile classes --- Emby.Dlna/Didl/DidlBuilder.cs | 26 +++-- .../Controllers/UniversalAudioController.cs | 6 +- MediaBrowser.Model/Dlna/ContainerProfile.cs | 63 ++++------- MediaBrowser.Model/Dlna/DeviceProfile.cs | 104 ++++++++---------- MediaBrowser.Model/Dlna/DirectPlayProfile.cs | 11 +- MediaBrowser.Model/Dlna/TranscodingProfile.cs | 27 ++++- 6 files changed, 114 insertions(+), 123 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 66ae07329..c66bdbf22 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -976,15 +976,28 @@ namespace Emby.Dlna.Didl return; } - var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg"); + // TODO: Remove these default values + var albumArtUrlInfo = GetImageUrl( + imageInfo, + _profile.MaxAlbumArtWidth ?? 10000, + _profile.MaxAlbumArtHeight ?? 10000, + "jpg"); writer.WriteStartElement("upnp", "albumArtURI", NsUpnp); - writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn); - writer.WriteString(albumartUrlInfo.url); + if (!string.IsNullOrEmpty(_profile.AlbumArtPn)) + { + writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn); + } + + writer.WriteString(albumArtUrlInfo.url); writer.WriteFullEndElement(); - // TOOD: Remove these default values - var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg"); + // TODO: Remove these default values + var iconUrlInfo = GetImageUrl( + imageInfo, + _profile.MaxIconWidth ?? 48, + _profile.MaxIconHeight ?? 48, + "jpg"); writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.url); if (!_profile.EnableAlbumArtInDidl) @@ -1207,8 +1220,7 @@ namespace Emby.Dlna.Didl if (width.HasValue && height.HasValue) { - var newSize = DrawingUtils.Resize( - new ImageDimensions(width.Value, height.Value), 0, 0, maxWidth, maxHeight); + var newSize = DrawingUtils.Resize(new ImageDimensions(width.Value, height.Value), 0, 0, maxWidth, maxHeight); width = newSize.Width; height = newSize.Height; diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index dcdd8b367..679f055bc 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -298,9 +298,9 @@ namespace Jellyfin.Api.Controllers { Type = DlnaProfileType.Audio, Context = EncodingContext.Streaming, - Container = transcodingContainer, - AudioCodec = audioCodec, - Protocol = transcodingProtocol, + Container = transcodingContainer ?? "mp3", + AudioCodec = audioCodec ?? "mp3", + Protocol = transcodingProtocol ?? "http", BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false, MaxAudioChannels = transcodingAudioChannels?.ToString(CultureInfo.InvariantCulture) } diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs index d83c8f2f3..54d4f7f38 100644 --- a/MediaBrowser.Model/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -1,7 +1,7 @@ -#nullable disable #pragma warning disable CS1591 using System; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Xml.Serialization; @@ -9,25 +9,17 @@ namespace MediaBrowser.Model.Dlna { public class ContainerProfile { - public ContainerProfile() - { - Conditions = Array.Empty(); - } - + [Required] [XmlAttribute("type")] public DlnaProfileType Type { get; set; } - public ProfileCondition[] Conditions { get; set; } + public ProfileCondition[]? Conditions { get; set; } = Array.Empty(); + [Required] [XmlAttribute("container")] - public string Container { get; set; } + public string Container { get; set; } = string.Empty; - public string[] GetContainers() - { - return SplitValue(Container); - } - - public static string[] SplitValue(string value) + public static string[] SplitValue(string? value) { if (string.IsNullOrEmpty(value)) { @@ -37,14 +29,14 @@ namespace MediaBrowser.Model.Dlna return value.Split(',', StringSplitOptions.RemoveEmptyEntries); } - public bool ContainsContainer(string container) + public bool ContainsContainer(string? container) { - var containers = GetContainers(); + var containers = SplitValue(Container); return ContainsContainer(containers, container); } - public static bool ContainsContainer(string profileContainers, string inputContainer) + public static bool ContainsContainer(string? profileContainers, string? inputContainer) { var isNegativeList = false; if (profileContainers != null && profileContainers.StartsWith('-')) @@ -56,46 +48,29 @@ namespace MediaBrowser.Model.Dlna return ContainsContainer(SplitValue(profileContainers), isNegativeList, inputContainer); } - public static bool ContainsContainer(string[] profileContainers, string inputContainer) + public static bool ContainsContainer(string[]? profileContainers, string? inputContainer) { return ContainsContainer(profileContainers, false, inputContainer); } - public static bool ContainsContainer(string[] profileContainers, bool isNegativeList, string inputContainer) + public static bool ContainsContainer(string[]? profileContainers, bool isNegativeList, string? inputContainer) { - if (profileContainers.Length == 0) + if (profileContainers == null || profileContainers.Length == 0) { - return true; + return isNegativeList; } - if (isNegativeList) - { - var allInputContainers = SplitValue(inputContainer); + var allInputContainers = SplitValue(inputContainer); - foreach (var container in allInputContainers) + foreach (var container in allInputContainers) + { + if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase)) { - if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase)) - { - return false; - } + return !isNegativeList; } - - return true; } - else - { - var allInputContainers = SplitValue(inputContainer); - foreach (var container in allInputContainers) - { - if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } + return isNegativeList; } } } diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index ff5186658..6ec4cb547 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -1,6 +1,6 @@ -#nullable disable #pragma warning disable CA1819 // Properties should not return arrays using System; +using System.ComponentModel; using System.Linq; using System.Xml.Serialization; using MediaBrowser.Model.MediaInfo; @@ -13,121 +13,104 @@ namespace MediaBrowser.Model.Dlna [XmlRoot("Profile")] public class DeviceProfile { - /// - /// Initializes a new instance of the class. - /// - public DeviceProfile() - { - DirectPlayProfiles = Array.Empty(); - TranscodingProfiles = Array.Empty(); - ResponseProfiles = Array.Empty(); - CodecProfiles = Array.Empty(); - ContainerProfiles = Array.Empty(); - SubtitleProfiles = Array.Empty(); - - XmlRootAttributes = Array.Empty(); - - SupportedMediaTypes = "Audio,Photo,Video"; - MaxStreamingBitrate = 8000000; - MaxStaticBitrate = 8000000; - MusicStreamingTranscodingBitrate = 128000; - } - /// /// Gets or sets the Name. /// - public string Name { get; set; } + public string? Name { get; set; } /// /// Gets or sets the Id. /// [XmlIgnore] - public string Id { get; set; } + public string? Id { get; set; } /// /// Gets or sets the Identification. /// - public DeviceIdentification Identification { get; set; } + public DeviceIdentification? Identification { get; set; } /// /// Gets or sets the FriendlyName. /// - public string FriendlyName { get; set; } + public string? FriendlyName { get; set; } /// /// Gets or sets the Manufacturer. /// - public string Manufacturer { get; set; } + public string? Manufacturer { get; set; } /// /// Gets or sets the ManufacturerUrl. /// - public string ManufacturerUrl { get; set; } + public string? ManufacturerUrl { get; set; } /// /// Gets or sets the ModelName. /// - public string ModelName { get; set; } + public string? ModelName { get; set; } /// /// Gets or sets the ModelDescription. /// - public string ModelDescription { get; set; } + public string? ModelDescription { get; set; } /// /// Gets or sets the ModelNumber. /// - public string ModelNumber { get; set; } + public string? ModelNumber { get; set; } /// /// Gets or sets the ModelUrl. /// - public string ModelUrl { get; set; } + public string? ModelUrl { get; set; } /// /// Gets or sets the SerialNumber. /// - public string SerialNumber { get; set; } + public string? SerialNumber { get; set; } /// /// Gets or sets a value indicating whether EnableAlbumArtInDidl. /// + [DefaultValue(false)] public bool EnableAlbumArtInDidl { get; set; } /// /// Gets or sets a value indicating whether EnableSingleAlbumArtLimit. /// + [DefaultValue(false)] public bool EnableSingleAlbumArtLimit { get; set; } /// /// Gets or sets a value indicating whether EnableSingleSubtitleLimit. /// + [DefaultValue(false)] public bool EnableSingleSubtitleLimit { get; set; } /// /// Gets or sets the SupportedMediaTypes. /// - public string SupportedMediaTypes { get; set; } + public string SupportedMediaTypes { get; set; } = "Audio,Photo,Video"; /// /// Gets or sets the UserId. /// - public string UserId { get; set; } + public string? UserId { get; set; } /// /// Gets or sets the AlbumArtPn. /// - public string AlbumArtPn { get; set; } + public string? AlbumArtPn { get; set; } /// /// Gets or sets the MaxAlbumArtWidth. /// - public int MaxAlbumArtWidth { get; set; } + public int? MaxAlbumArtWidth { get; set; } /// /// Gets or sets the MaxAlbumArtHeight. /// - public int MaxAlbumArtHeight { get; set; } + public int? MaxAlbumArtHeight { get; set; } /// /// Gets or sets the MaxIconWidth. @@ -142,92 +125,97 @@ namespace MediaBrowser.Model.Dlna /// /// Gets or sets the MaxStreamingBitrate. /// - public int? MaxStreamingBitrate { get; set; } + public int? MaxStreamingBitrate { get; set; } = 8000000; /// /// Gets or sets the MaxStaticBitrate. /// - public int? MaxStaticBitrate { get; set; } + public int? MaxStaticBitrate { get; set; } = 8000000; /// /// Gets or sets the MusicStreamingTranscodingBitrate. /// - public int? MusicStreamingTranscodingBitrate { get; set; } + public int? MusicStreamingTranscodingBitrate { get; set; } = 128000; /// /// Gets or sets the MaxStaticMusicBitrate. /// - public int? MaxStaticMusicBitrate { get; set; } + public int? MaxStaticMusicBitrate { get; set; } = 8000000; /// /// Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace. /// - public string SonyAggregationFlags { get; set; } + public string? SonyAggregationFlags { get; set; } /// /// Gets or sets the ProtocolInfo. /// - public string ProtocolInfo { get; set; } + public string? ProtocolInfo { get; set; } /// /// Gets or sets the TimelineOffsetSeconds. /// + [DefaultValue(0)] public int TimelineOffsetSeconds { get; set; } /// /// Gets or sets a value indicating whether RequiresPlainVideoItems. /// + [DefaultValue(false)] public bool RequiresPlainVideoItems { get; set; } /// /// Gets or sets a value indicating whether RequiresPlainFolders. /// + [DefaultValue(false)] public bool RequiresPlainFolders { get; set; } /// /// Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar. /// + [DefaultValue(false)] public bool EnableMSMediaReceiverRegistrar { get; set; } /// /// Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests. /// + [DefaultValue(false)] public bool IgnoreTranscodeByteRangeRequests { get; set; } /// /// Gets or sets the XmlRootAttributes. /// - public XmlAttribute[] XmlRootAttributes { get; set; } + public XmlAttribute[] XmlRootAttributes { get; set; } = Array.Empty(); /// /// Gets or sets the direct play profiles. /// - public DirectPlayProfile[] DirectPlayProfiles { get; set; } + public DirectPlayProfile[] DirectPlayProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the transcoding profiles. /// - public TranscodingProfile[] TranscodingProfiles { get; set; } + public TranscodingProfile[] TranscodingProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the ContainerProfiles. /// - public ContainerProfile[] ContainerProfiles { get; set; } + public ContainerProfile[] ContainerProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the CodecProfiles. /// - public CodecProfile[] CodecProfiles { get; set; } + public CodecProfile[] CodecProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the ResponseProfiles. /// - public ResponseProfile[] ResponseProfiles { get; set; } + public ResponseProfile[] ResponseProfiles { get; set; } = Array.Empty(); /// /// Gets or sets the SubtitleProfiles. /// - public SubtitleProfile[] SubtitleProfiles { get; set; } + public SubtitleProfile[] SubtitleProfiles { get; set; } = Array.Empty(); /// /// The GetSupportedMediaTypes. @@ -244,13 +232,13 @@ namespace MediaBrowser.Model.Dlna /// The container. /// The audio Codec. /// A . - public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec) + public TranscodingProfile? GetAudioTranscodingProfile(string? container, string? audioCodec) { container = (container ?? string.Empty).TrimStart('.'); foreach (var i in TranscodingProfiles) { - if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio) + if (i.Type != DlnaProfileType.Audio) { continue; } @@ -278,13 +266,13 @@ namespace MediaBrowser.Model.Dlna /// The audio Codec. /// The video Codec. /// The . - public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec) + public TranscodingProfile? GetVideoTranscodingProfile(string? container, string? audioCodec, string? videoCodec) { container = (container ?? string.Empty).TrimStart('.'); foreach (var i in TranscodingProfiles) { - if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video) + if (i.Type != DlnaProfileType.Video) { continue; } @@ -299,7 +287,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (!string.Equals(videoCodec, i.VideoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase)) { continue; } @@ -320,7 +308,7 @@ namespace MediaBrowser.Model.Dlna /// The audio sample rate. /// The audio bit depth. /// The . - public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) + public ResponseProfile? GetAudioMediaProfile(string container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) { foreach (var i in ResponseProfiles) { @@ -384,7 +372,7 @@ namespace MediaBrowser.Model.Dlna /// The width. /// The height. /// The . - public ResponseProfile GetImageMediaProfile(string container, int? width, int? height) + public ResponseProfile? GetImageMediaProfile(string container, int? width, int? height) { foreach (var i in ResponseProfiles) { @@ -442,7 +430,7 @@ namespace MediaBrowser.Model.Dlna /// The video Codec tag. /// True if Avc. /// The . - public ResponseProfile GetVideoMediaProfile( + public ResponseProfile? GetVideoMediaProfile( string container, string audioCodec, string videoCodec, diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs index 88cb83991..417d915b5 100644 --- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs +++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs @@ -1,6 +1,6 @@ -#nullable disable #pragma warning disable CS1591 +using System.ComponentModel.DataAnnotations; using System.Xml.Serialization; namespace MediaBrowser.Model.Dlna @@ -8,14 +8,15 @@ namespace MediaBrowser.Model.Dlna public class DirectPlayProfile { [XmlAttribute("container")] - public string Container { get; set; } + public string? Container { get; set; } [XmlAttribute("audioCodec")] - public string AudioCodec { get; set; } + public string? AudioCodec { get; set; } [XmlAttribute("videoCodec")] - public string VideoCodec { get; set; } + public string? VideoCodec { get; set; } + [Required] [XmlAttribute("type")] public DlnaProfileType Type { get; set; } @@ -31,7 +32,7 @@ namespace MediaBrowser.Model.Dlna public bool SupportsAudioCodec(string codec) { - return (Type == DlnaProfileType.Audio || Type == DlnaProfileType.Video) && ContainerProfile.ContainsContainer(AudioCodec, codec); + return (Type is DlnaProfileType.Audio or DlnaProfileType.Video) && ContainerProfile.ContainsContainer(AudioCodec, codec); } } } diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index f05e31047..ea446b733 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -1,54 +1,69 @@ -#nullable disable #pragma warning disable CS1591 +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Xml.Serialization; namespace MediaBrowser.Model.Dlna { public class TranscodingProfile { + [Required] [XmlAttribute("container")] - public string Container { get; set; } + public string Container { get; set; } = string.Empty; + [Required] [XmlAttribute("type")] public DlnaProfileType Type { get; set; } + [Required] [XmlAttribute("videoCodec")] - public string VideoCodec { get; set; } + public string VideoCodec { get; set; } = string.Empty; + [Required] [XmlAttribute("audioCodec")] - public string AudioCodec { get; set; } + public string AudioCodec { get; set; } = string.Empty; + [Required] [XmlAttribute("protocol")] - public string Protocol { get; set; } + public string Protocol { get; set; } = string.Empty; + [DefaultValue(false)] [XmlAttribute("estimateContentLength")] public bool EstimateContentLength { get; set; } + [DefaultValue(false)] [XmlAttribute("enableMpegtsM2TsMode")] public bool EnableMpegtsM2TsMode { get; set; } + [DefaultValue(TranscodeSeekInfo.Auto)] [XmlAttribute("transcodeSeekInfo")] public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + [DefaultValue(false)] [XmlAttribute("copyTimestamps")] public bool CopyTimestamps { get; set; } + [DefaultValue(EncodingContext.Streaming)] [XmlAttribute("context")] public EncodingContext Context { get; set; } + [DefaultValue(false)] [XmlAttribute("enableSubtitlesInManifest")] public bool EnableSubtitlesInManifest { get; set; } [XmlAttribute("maxAudioChannels")] - public string MaxAudioChannels { get; set; } + public string? MaxAudioChannels { get; set; } + [DefaultValue(0)] [XmlAttribute("minSegments")] public int MinSegments { get; set; } + [DefaultValue(0)] [XmlAttribute("segmentLength")] public int SegmentLength { get; set; } + [DefaultValue(false)] [XmlAttribute("breakOnNonKeyFrames")] public bool BreakOnNonKeyFrames { get; set; } From c608d5104df7b7281e35595552734d77e42b5036 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 1 May 2021 15:56:16 +0200 Subject: [PATCH 364/402] Fix scanning --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 2 -- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- Emby.Server.Implementations/Library/ResolverHelper.cs | 3 +-- MediaBrowser.Controller/Library/ItemResolveArgs.cs | 2 +- MediaBrowser.Model/IO/FileSystemMetadata.cs | 6 ------ 5 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index df973f971..27096ed33 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -260,8 +260,6 @@ namespace Emby.Server.Implementations.IO result.Exists = false; } } - - result.DirectoryName = fileInfo.DirectoryName; } result.CreationTimeUtc = GetCreationTimeUtc(info); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index ec59ca9b9..7629676f5 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -683,7 +683,7 @@ namespace Emby.Server.Implementations.Library foreach (var item in items) { - ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService); + ResolverHelper.SetInitialItemValues(item, parent, this, directoryService); } items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions)); diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index 8be80d726..b1a2e9284 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -20,11 +20,10 @@ namespace Emby.Server.Implementations.Library /// /// The item. /// The parent. - /// The file system. /// The library manager. /// The directory service. /// Item must have a path. - public static void SetInitialItemValues(BaseItem item, Folder? parent, IFileSystem fileSystem, ILibraryManager libraryManager, IDirectoryService directoryService) + public static void SetInitialItemValues(BaseItem item, Folder? parent, ILibraryManager libraryManager, IDirectoryService directoryService) { // This version of the below method has no ItemResolveArgs, so we have to require the path already being set if (string.IsNullOrEmpty(item.Path)) diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index df8842237..f86f7df25 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -87,7 +87,7 @@ namespace MediaBrowser.Controller.Library return false; } - var parentDir = FileInfo.DirectoryName ?? string.Empty; + var parentDir = System.IO.Path.GetDirectoryName(Path) ?? string.Empty; return parentDir.Length > _appPaths.RootFolderPath.Length && parentDir.StartsWith(_appPaths.RootFolderPath, StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Model/IO/FileSystemMetadata.cs b/MediaBrowser.Model/IO/FileSystemMetadata.cs index 118c78e80..fb74886bf 100644 --- a/MediaBrowser.Model/IO/FileSystemMetadata.cs +++ b/MediaBrowser.Model/IO/FileSystemMetadata.cs @@ -37,12 +37,6 @@ namespace MediaBrowser.Model.IO /// The length. public long Length { get; set; } - /// - /// Gets or sets the name of the directory. - /// - /// The name of the directory. - public string DirectoryName { get; set; } - /// /// Gets or sets the last write time UTC. /// From e128b6d9978dc219060fe27bc4de0e73495822c2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 1 May 2021 15:59:21 +0200 Subject: [PATCH 365/402] TmdbUtils: Use ordinal string compare --- MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index 15a44c7ed..2498ce9c4 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -63,19 +63,19 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// The Jellyfin person type. public static string MapCrewToPersonType(Crew crew) { - if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) - && crew.Job.Contains("director", StringComparison.InvariantCultureIgnoreCase)) + if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase) + && crew.Job.Contains("director", StringComparison.OrdinalIgnoreCase)) { return PersonType.Director; } - if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) - && crew.Job.Contains("producer", StringComparison.InvariantCultureIgnoreCase)) + if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase) + && crew.Job.Contains("producer", StringComparison.OrdinalIgnoreCase)) { return PersonType.Producer; } - if (crew.Department.Equals("writing", StringComparison.InvariantCultureIgnoreCase)) + if (crew.Department.Equals("writing", StringComparison.OrdinalIgnoreCase)) { return PersonType.Writer; } From 8a6b9e1fb6d89ac77bd8d62da66bd05abd75ca65 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 2 May 2021 00:26:30 +0200 Subject: [PATCH 366/402] Add tests for SqliteItemRepository.(De)SerializeImages --- .../Data/SqliteItemRepository.cs | 41 ++--- .../Data/SqliteItemRepositoryTests.cs | 172 ++++++++++++++++++ 2 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 28e59913c..9b558d34b 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -502,7 +502,7 @@ namespace Emby.Server.Implementations.Data using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) { saveImagesStatement.TryBind("@Id", item.Id.ToByteArray()); - saveImagesStatement.TryBind("@Images", SerializeImages(item)); + saveImagesStatement.TryBind("@Images", SerializeImages(item.ImageInfos)); saveImagesStatement.MoveNext(); } @@ -898,7 +898,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@Tagline", item.Tagline); saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item)); - saveItemStatement.TryBind("@Images", SerializeImages(item)); + saveItemStatement.TryBind("@Images", SerializeImages(item.ImageInfos)); if (item.ProductionLocations.Length > 0) { @@ -1020,10 +1020,8 @@ namespace Emby.Server.Implementations.Data } } - private string SerializeImages(BaseItem item) + internal string SerializeImages(ItemImageInfo[] images) { - var images = item.ImageInfos; - if (images.Length == 0) { return null; @@ -1045,16 +1043,11 @@ namespace Emby.Server.Implementations.Data return str.ToString(); } - private void DeserializeImages(string value, BaseItem item) + internal ItemImageInfo[] DeserializeImages(string value) { if (string.IsNullOrWhiteSpace(value)) { - return; - } - - if (item.ImageInfos.Length > 0) - { - return; + return Array.Empty(); } var parts = value.Split('|' , StringSplitOptions.RemoveEmptyEntries); @@ -1069,15 +1062,14 @@ namespace Emby.Server.Implementations.Data } } - item.ImageInfos = list.ToArray(); + return list.ToArray(); } - public void AppendItemImageInfo(StringBuilder bldr, ItemImageInfo image) + private void AppendItemImageInfo(StringBuilder bldr, ItemImageInfo image) { const char Delimiter = '*'; var path = image.Path ?? string.Empty; - var hash = image.BlurHash ?? string.Empty; bldr.Append(GetPathToSave(path)) .Append(Delimiter) @@ -1087,11 +1079,16 @@ namespace Emby.Server.Implementations.Data .Append(Delimiter) .Append(image.Width) .Append(Delimiter) - .Append(image.Height) - .Append(Delimiter) - // Replace delimiters with other characters. - // This can be removed when we migrate to a proper DB. - .Append(hash.Replace('*', '/').Replace('|', '\\')); + .Append(image.Height); + + var hash = image.BlurHash; + if (!string.IsNullOrEmpty(hash)) + { + bldr.Append(Delimiter) + // Replace delimiters with other characters. + // This can be removed when we migrate to a proper DB. + .Append(hash.Replace('*', '/').Replace('|', '\\')); + } } public ItemImageInfo ItemImageInfoFromValueString(string value) @@ -1797,11 +1794,11 @@ namespace Emby.Server.Implementations.Data index++; - if (query.DtoOptions.EnableImages) + if (query.DtoOptions.EnableImages && item.ImageInfos.Length == 0) { if (!reader.IsDBNull(index)) { - DeserializeImages(reader.GetString(index), item); + item.ImageInfos = DeserializeImages(reader.GetString(index)); } index++; diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs new file mode 100644 index 000000000..e9cfd5b12 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using AutoFixture; +using AutoFixture.AutoMoq; +using Emby.Server.Implementations.Data; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; +using Moq; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Data +{ + public class SqliteItemRepositoryTests + { + public const string VirtualMetaDataPath = "%MetadataPath%"; + public const string MetaDataPath = "/meta/data/path"; + + private readonly IFixture _fixture; + private readonly SqliteItemRepository _sqliteItemRepository; + + public SqliteItemRepositoryTests() + { + var appHost = new Mock(); + appHost.Setup(x => x.ExpandVirtualPath(It.IsAny())) + .Returns((string x) => x.Replace(VirtualMetaDataPath, MetaDataPath, StringComparison.Ordinal)); + appHost.Setup(x => x.ReverseVirtualPath(It.IsAny())) + .Returns((string x) => x.Replace(MetaDataPath, VirtualMetaDataPath, StringComparison.Ordinal)); + + _fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true }); + _fixture.Inject(appHost); + _sqliteItemRepository = _fixture.Create(); + } + + public static IEnumerable ItemImageInfoFromValueString_Valid_TestData() + { + yield return new object[] + { + "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN", + new ItemImageInfo() + { + Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg", + Type = ImageType.Primary, + DateModified = new DateTime(637452096478512963, DateTimeKind.Utc), + Width = 1920, + Height = 1080, + BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN" + } + }; + + yield return new object[] + { + "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*0*0", + new ItemImageInfo() + { + Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg", + Type = ImageType.Primary, + } + }; + + yield return new object[] + { + "%MetadataPath%/library/68/68578562b96c80a7ebd530848801f645/poster.jpg*637264380567586027*Primary*600*336", + new ItemImageInfo() + { + Path = "/meta/data/path/library/68/68578562b96c80a7ebd530848801f645/poster.jpg", + Type = ImageType.Primary, + DateModified = new DateTime(637264380567586027, DateTimeKind.Utc), + Width = 600, + Height = 336 + } + }; + } + + [Theory] + [MemberData(nameof(ItemImageInfoFromValueString_Valid_TestData))] + public void ItemImageInfoFromValueString_Valid_Success(string value, ItemImageInfo expected) + { + var result = _sqliteItemRepository.ItemImageInfoFromValueString(value); + Assert.Equal(expected.Path, result.Path); + Assert.Equal(expected.Type, result.Type); + Assert.Equal(expected.DateModified, result.DateModified); + Assert.Equal(expected.Width, result.Width); + Assert.Equal(expected.Height, result.Height); + Assert.Equal(expected.BlurHash, result.BlurHash); + } + + [Theory] + [InlineData("")] + [InlineData("*")] + public void ItemImageInfoFromValueString_Invalid_Null(string value) + { + Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value)); + } + + public static IEnumerable DeserializeImages_Valid_TestData() + { + yield return new object[] + { + "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN", + new ItemImageInfo[] + { + new ItemImageInfo() + { + Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg", + Type = ImageType.Primary, + DateModified = new DateTime(637452096478512963, DateTimeKind.Utc), + Width = 1920, + Height = 1080, + BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN" + } + } + }; + + yield return new object[] + { + "%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg*637261226720645297*Primary*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png*637261226720805297*Logo*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg*637261226721285297*Thumb*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg*637261226721685297*Backdrop*0*0", + new ItemImageInfo[] + { + new ItemImageInfo() + { + Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg", + Type = ImageType.Primary, + DateModified = new DateTime(637261226720645297, DateTimeKind.Utc), + }, + new ItemImageInfo() + { + Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png", + Type = ImageType.Logo, + DateModified = new DateTime(637261226720805297, DateTimeKind.Utc), + }, + new ItemImageInfo() + { + Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg", + Type = ImageType.Thumb, + DateModified = new DateTime(637261226721285297, DateTimeKind.Utc), + }, + new ItemImageInfo() + { + Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg", + Type = ImageType.Backdrop, + DateModified = new DateTime(637261226721685297, DateTimeKind.Utc), + } + } + }; + } + + [Theory] + [MemberData(nameof(DeserializeImages_Valid_TestData))] + public void DeserializeImages_Valid_Success(string value, ItemImageInfo[] expected) + { + var result = _sqliteItemRepository.DeserializeImages(value); + Assert.Equal(expected.Length, result.Length); + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i].Path, result[i].Path); + Assert.Equal(expected[i].Type, result[i].Type); + Assert.Equal(expected[i].DateModified, result[i].DateModified); + Assert.Equal(expected[i].Width, result[i].Width); + Assert.Equal(expected[i].Height, result[i].Height); + Assert.Equal(expected[i].BlurHash, result[i].BlurHash); + } + } + + [Theory] + [MemberData(nameof(DeserializeImages_Valid_TestData))] + public void SerializeImages_Valid_Success(string expected, ItemImageInfo[] value) + { + Assert.Equal(expected, _sqliteItemRepository.SerializeImages(value)); + } + } +} From dc81d576ab78cc6e666f768343cb6d29e5820670 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 2 May 2021 01:20:58 -0400 Subject: [PATCH 367/402] Remove /Images/Remote API endpoint --- .../Controllers/RemoteImageController.cs | 52 ------------------- 1 file changed, 52 deletions(-) diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index e226adc64..ec836f43e 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -145,58 +145,6 @@ namespace Jellyfin.Api.Controllers return Ok(_providerManager.GetRemoteImageProviderInfo(item)); } - /// - /// Gets a remote image. - /// - /// The image url. - /// Remote image returned. - /// Remote image not found. - /// Image Stream. - [HttpGet("Images/Remote")] - [Produces(MediaTypeNames.Application.Octet)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesImageFile] - public async Task GetRemoteImage([FromQuery, Required] Uri imageUrl) - { - var urlHash = imageUrl.ToString().GetMD5(); - var pointerCachePath = GetFullCachePath(urlHash.ToString()); - - string? contentPath = null; - var hasFile = false; - - try - { - contentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false); - if (System.IO.File.Exists(contentPath)) - { - hasFile = true; - } - } - catch (FileNotFoundException) - { - // The file isn't cached yet - } - catch (IOException) - { - // The file isn't cached yet - } - - if (!hasFile) - { - await DownloadImage(imageUrl, urlHash, pointerCachePath).ConfigureAwait(false); - contentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false); - } - - if (string.IsNullOrEmpty(contentPath)) - { - return NotFound(); - } - - var contentType = MimeTypes.GetMimeType(contentPath); - return PhysicalFile(contentPath, contentType); - } - /// /// Downloads a remote image for an item. /// From 3e4c86098613d92eb2fc34ee158459c0d063f910 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 2 May 2021 01:22:52 -0400 Subject: [PATCH 368/402] Remove /Items/RemoteSearch/Image API endpoint --- .../Controllers/ItemLookupController.cs | 91 ------------------- 1 file changed, 91 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index dabd4deb7..9fa307858 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -237,48 +237,6 @@ namespace Jellyfin.Api.Controllers return Ok(results); } - /// - /// Gets a remote image. - /// - /// The image url. - /// The provider name. - /// Remote image retrieved. - /// - /// A that represents the asynchronous operation to get the remote search results. - /// The task result contains an containing the images file stream. - /// - [HttpGet("Items/RemoteSearch/Image")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesImageFile] - public async Task GetRemoteSearchImage( - [FromQuery, Required] string imageUrl, - [FromQuery, Required] string providerName) - { - var urlHash = imageUrl.GetMD5(); - var pointerCachePath = GetFullCachePath(urlHash.ToString()); - - try - { - var contentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false); - if (System.IO.File.Exists(contentPath)) - { - return PhysicalFile(contentPath, MimeTypes.GetMimeType(contentPath)); - } - } - catch (FileNotFoundException) - { - // Means the file isn't cached yet - } - catch (IOException) - { - // Means the file isn't cached yet - } - - await DownloadImage(providerName, imageUrl, urlHash, pointerCachePath).ConfigureAwait(false); - var updatedContentPath = await System.IO.File.ReadAllTextAsync(pointerCachePath).ConfigureAwait(false); - return PhysicalFile(updatedContentPath, MimeTypes.GetMimeType(updatedContentPath)); - } - /// /// Applies search criteria to an item and refreshes metadata. /// @@ -320,54 +278,5 @@ namespace Jellyfin.Api.Controllers return NoContent(); } - - /// - /// Downloads the image. - /// - /// Name of the provider. - /// The URL. - /// The URL hash. - /// The pointer cache path. - /// Task. - private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath) - { - using var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false); - if (result.Content.Headers.ContentType?.MediaType == null) - { - throw new ResourceNotFoundException(nameof(result.Content.Headers.ContentType)); - } - - var ext = result.Content.Headers.ContentType.MediaType.Split('/')[^1]; - var fullCachePath = GetFullCachePath(urlHash + "." + ext); - - var directory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid."); - Directory.CreateDirectory(directory); - using (var stream = result.Content) - { - // use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 . - await using var fileStream = new FileStream( - fullCachePath, - FileMode.Create, - FileAccess.Write, - FileShare.None, - IODefaults.FileStreamBufferSize, - true); - - await stream.CopyToAsync(fileStream).ConfigureAwait(false); - } - - var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath)); - - Directory.CreateDirectory(pointerCacheDirectory); - await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath).ConfigureAwait(false); - } - - /// - /// Gets the full cache path. - /// - /// The filename. - /// System.String. - private string GetFullCachePath(string filename) - => Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename); } } From 874f92e93a1411fbba7097f0665db68234441dfa Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 2 May 2021 12:45:02 +0200 Subject: [PATCH 369/402] Add tests for SqliteItemRepository.(De)SerializeProviderIds --- .../Data/SqliteItemRepository.cs | 19 +++---- .../Data/SqliteItemRepositoryTests.cs | 57 +++++++++++++++++++ 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 9b558d34b..786e3b6fa 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -897,7 +897,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId); saveItemStatement.TryBind("@Tagline", item.Tagline); - saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item)); + saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item.ProviderIds)); saveItemStatement.TryBind("@Images", SerializeImages(item.ImageInfos)); if (item.ProductionLocations.Length > 0) @@ -968,10 +968,10 @@ namespace Emby.Server.Implementations.Data saveItemStatement.MoveNext(); } - private static string SerializeProviderIds(BaseItem item) + internal static string SerializeProviderIds(Dictionary providerIds) { StringBuilder str = new StringBuilder(); - foreach (var i in item.ProviderIds) + foreach (var i in providerIds) { // Ideally we shouldn't need this IsNullOrWhiteSpace check, // but we're seeing some cases of bad data slip through @@ -995,18 +995,13 @@ namespace Emby.Server.Implementations.Data return str.ToString(); } - private static void DeserializeProviderIds(string value, BaseItem item) + internal static void DeserializeProviderIds(string value, IHasProviderIds item) { if (string.IsNullOrWhiteSpace(value)) { return; } - if (item.ProviderIds.Count > 0) - { - return; - } - var parts = value.Split('|', StringSplitOptions.RemoveEmptyEntries); foreach (var part in parts) @@ -1787,16 +1782,16 @@ namespace Emby.Server.Implementations.Data index++; } - if (!reader.IsDBNull(index)) + if (item.ProviderIds.Count == 0 && !reader.IsDBNull(index)) { DeserializeProviderIds(reader.GetString(index), item); } index++; - if (query.DtoOptions.EnableImages && item.ImageInfos.Length == 0) + if (query.DtoOptions.EnableImages) { - if (!reader.IsDBNull(index)) + if (item.ImageInfos.Length == 0 && !reader.IsDBNull(index)) { item.ImageInfos = DeserializeImages(reader.GetString(index)); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs index e9cfd5b12..af6ec3245 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs @@ -168,5 +168,62 @@ namespace Jellyfin.Server.Implementations.Tests.Data { Assert.Equal(expected, _sqliteItemRepository.SerializeImages(value)); } + + public static IEnumerable DeserializeProviderIds_Valid_TestData() + { + yield return new object[] + { + "Imdb=tt0119567", + new Dictionary() + { + { "Imdb", "tt0119567" }, + } + }; + + yield return new object[] + { + "Imdb=tt0119567|Tmdb=330|TmdbCollection=328", + new Dictionary() + { + { "Imdb", "tt0119567" }, + { "Tmdb", "330" }, + { "TmdbCollection", "328" }, + } + }; + + yield return new object[] + { + "MusicBrainzAlbum=9d363e43-f24f-4b39-bc5a-7ef305c677c7|MusicBrainzReleaseGroup=63eba062-847c-3b73-8b0f-6baf27bba6fa|AudioDbArtist=111352|AudioDbAlbum=2116560|MusicBrainzAlbumArtist=20244d07-534f-4eff-b4d4-930878889970", + new Dictionary() + { + { "MusicBrainzAlbum", "9d363e43-f24f-4b39-bc5a-7ef305c677c7" }, + { "MusicBrainzReleaseGroup", "63eba062-847c-3b73-8b0f-6baf27bba6fa" }, + { "AudioDbArtist", "111352" }, + { "AudioDbAlbum", "2116560" }, + { "MusicBrainzAlbumArtist", "20244d07-534f-4eff-b4d4-930878889970" }, + } + }; + } + + [Theory] + [MemberData(nameof(DeserializeProviderIds_Valid_TestData))] + public void DeserializeProviderIds_Valid_Success(string value, Dictionary expected) + { + var result = new ProviderIdsExtensionsTestsObject(); + SqliteItemRepository.DeserializeProviderIds(value, result); + Assert.Equal(expected, result.ProviderIds); + } + + [Theory] + [MemberData(nameof(DeserializeProviderIds_Valid_TestData))] + public void SerializeProviderIds_Valid_Success(string expected, Dictionary values) + { + Assert.Equal(expected, SqliteItemRepository.SerializeProviderIds(values)); + } + + private class ProviderIdsExtensionsTestsObject : IHasProviderIds + { + public Dictionary ProviderIds { get; set; } = new Dictionary(); + } } } From bdd7a37794518b0487b191e7dbbbd3beb2397458 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 2 May 2021 13:04:35 +0200 Subject: [PATCH 370/402] Don't run integration tests in parallel * Easier to debug failing tests when the logs aren't scrambled * Static properties could cause issues --- .../Jellyfin.Server.Integration.Tests.csproj | 7 +++++++ tests/Jellyfin.Server.Integration.Tests/xunit.runner.json | 4 ++++ 2 files changed, 11 insertions(+) create mode 100644 tests/Jellyfin.Server.Integration.Tests/xunit.runner.json diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index c1d871126..938385a2a 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -22,6 +22,13 @@ + + + + PreserveNewest + + + diff --git a/tests/Jellyfin.Server.Integration.Tests/xunit.runner.json b/tests/Jellyfin.Server.Integration.Tests/xunit.runner.json new file mode 100644 index 000000000..809e880c7 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} From bcba501dfbe1e0f422829f26159d9a9625530231 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 2 May 2021 19:25:04 +0100 Subject: [PATCH 371/402] minor optimization. --- Emby.Server.Implementations/Plugins/PluginManager.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 3a8296455..fd2ee6b7a 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -166,9 +166,7 @@ namespace Emby.Server.Implementations.Plugins /// public void CreatePlugins() { - _ = _appHost.GetExports(CreatePluginInstance) - .Where(i => i != null) - .ToArray(); + _ = _appHost.GetExports(CreatePluginInstance); } /// From 6f722bac0f5b3fb019a8313753b198f34afa714c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 May 2021 12:00:39 +0000 Subject: [PATCH 372/402] Bump Swashbuckle.AspNetCore from 6.1.3 to 6.1.4 Bumps [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) from 6.1.3 to 6.1.4. - [Release notes](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases) - [Commits](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/compare/v6.1.3...v6.1.4) Signed-off-by: dependabot[bot] --- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 3868882e5..d6d07cd27 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + From 9b1243cf5ef21bffdd31054e35121d811688ba3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 May 2021 12:00:50 +0000 Subject: [PATCH 373/402] Bump SQLitePCL.pretty.netstandard from 2.1.0 to 2.2.0 Bumps [SQLitePCL.pretty.netstandard](https://github.com/jellyfin/SQLitePCL.pretty.netstandard) from 2.1.0 to 2.2.0. - [Release notes](https://github.com/jellyfin/SQLitePCL.pretty.netstandard/releases) - [Commits](https://github.com/jellyfin/SQLitePCL.pretty.netstandard/commits) Signed-off-by: dependabot[bot] --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index adbfe52c4..b8a544b8c 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -31,7 +31,7 @@ - + From 2ee33bd602fb17411774b278a3f104e5121fab13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 May 2021 12:03:25 +0000 Subject: [PATCH 374/402] Bump alex-page/github-project-automation-plus from v0.6.0 to v0.7.1 Bumps [alex-page/github-project-automation-plus](https://github.com/alex-page/github-project-automation-plus) from v0.6.0 to v0.7.1. - [Release notes](https://github.com/alex-page/github-project-automation-plus/releases) - [Commits](https://github.com/alex-page/github-project-automation-plus/compare/v0.6.0...50502d399cbb98cefe7ce1f99f93f78c6756562e) Signed-off-by: dependabot[bot] --- .github/workflows/automation.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml index d5d308185..a203e6695 100644 --- a/.github/workflows/automation.yml +++ b/.github/workflows/automation.yml @@ -16,7 +16,7 @@ jobs: label: stable backport - name: Remove from 'Current Release' project - uses: alex-page/github-project-automation-plus@v0.6.0 + uses: alex-page/github-project-automation-plus@v0.7.1 if: (github.event.pull_request || github.event.issue.pull_request) && !steps.checkLabel.outputs.hasLabel continue-on-error: true with: @@ -25,7 +25,7 @@ jobs: repo-token: ${{ secrets.GH_TOKEN }} - name: Add to 'Release Next' project - uses: alex-page/github-project-automation-plus@v0.6.0 + uses: alex-page/github-project-automation-plus@v0.7.1 if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened' continue-on-error: true with: @@ -34,7 +34,7 @@ jobs: repo-token: ${{ secrets.GH_TOKEN }} - name: Add to 'Current Release' project - uses: alex-page/github-project-automation-plus@v0.6.0 + uses: alex-page/github-project-automation-plus@v0.7.1 if: (github.event.pull_request || github.event.issue.pull_request) && steps.checkLabel.outputs.hasLabel continue-on-error: true with: @@ -48,7 +48,7 @@ jobs: run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)" - name: Move issue to needs triage - uses: alex-page/github-project-automation-plus@v0.6.0 + uses: alex-page/github-project-automation-plus@v0.7.1 if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1 continue-on-error: true with: @@ -57,7 +57,7 @@ jobs: repo-token: ${{ secrets.GH_TOKEN }} - name: Add issue to triage project - uses: alex-page/github-project-automation-plus@v0.6.0 + uses: alex-page/github-project-automation-plus@v0.7.1 if: github.event.issue.pull_request == '' && github.event.action == 'opened' continue-on-error: true with: From c0feb3694b86ccc242a637468070daff9120e631 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 3 May 2021 23:51:45 +0200 Subject: [PATCH 375/402] rename to SplitEnumerator and fix test --- .../Data/SqliteItemRepository.cs | 14 ++++++--- ...Extensions.cs => SplitStringExtensions.cs} | 16 +++++----- .../Data/SqliteItemRepositoryTests.cs | 31 ++++++++++++++++--- 3 files changed, 43 insertions(+), 18 deletions(-) rename MediaBrowser.Common/Extensions/{SplitLinesStringExtensions.cs => SplitStringExtensions.cs} (84%) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 292ff48fa..4b9b0bed0 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1082,7 +1082,7 @@ namespace Emby.Server.Implementations.Data } } - private ItemImageInfo ItemImageInfoFromValueString(ReadOnlySpan value) + internal ItemImageInfo ItemImageInfoFromValueString(ReadOnlySpan value) { var nextSegment = value.IndexOf('*'); if (nextSegment == -1) @@ -1103,7 +1103,7 @@ namespace Emby.Server.Implementations.Data nextSegment = value.IndexOf('*'); if (nextSegment == -1) { - return null; + nextSegment = value.Length; } ReadOnlySpan imageType = value[..nextSegment]; @@ -1128,13 +1128,18 @@ namespace Emby.Server.Implementations.Data { value = value[(nextSegment + 1)..]; nextSegment = value.IndexOf('*'); + if (nextSegment == -1 || nextSegment == value.Length) + { + return image; + } + ReadOnlySpan widthSpan = value[..nextSegment]; value = value[(nextSegment + 1)..]; nextSegment = value.IndexOf('*'); if (nextSegment == -1) { - return image; + nextSegment = value.Length; } ReadOnlySpan heightSpan = value[..nextSegment]; @@ -1146,10 +1151,9 @@ namespace Emby.Server.Implementations.Data image.Height = height; } - nextSegment += 1; if (nextSegment < value.Length - 1) { - value = value[nextSegment..]; + value = value[(nextSegment + 1)..]; var length = value.Length; Span blurHashSpan = stackalloc char[length]; diff --git a/MediaBrowser.Common/Extensions/SplitLinesStringExtensions.cs b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs similarity index 84% rename from MediaBrowser.Common/Extensions/SplitLinesStringExtensions.cs rename to MediaBrowser.Common/Extensions/SplitStringExtensions.cs index 5332aba9f..6c1c45013 100644 --- a/MediaBrowser.Common/Extensions/SplitLinesStringExtensions.cs +++ b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs @@ -33,33 +33,33 @@ namespace MediaBrowser.Common.Extensions /// /// Extension class for splitting lines without unnecessary allocations. /// - public static class SplitLinesStringExtensions + public static class SplitStringExtensions { /// - /// Creates a new line split enumerator. + /// Creates a new string split enumerator. /// /// The string to split. /// The separator to split on. /// The enumerator struct. [Pure] - public static LineSplitEnumerator SpanSplit(this string str, char separator) => new (str.AsSpan(), separator); + public static SplitEnumerator SpanSplit(this string str, char separator) => new (str.AsSpan(), separator); /// - /// Creates a new line split enumerator. + /// Creates a new span split enumerator. /// /// The span to split. /// The separator to split on. /// The enumerator struct. [Pure] - public static LineSplitEnumerator Split(this ReadOnlySpan str, char separator) => new (str, separator); + public static SplitEnumerator Split(this ReadOnlySpan str, char separator) => new (str, separator); [StructLayout(LayoutKind.Auto)] - public ref struct LineSplitEnumerator + public ref struct SplitEnumerator { private readonly char _separator; private ReadOnlySpan _str; - public LineSplitEnumerator(ReadOnlySpan str, char separator) + public SplitEnumerator(ReadOnlySpan str, char separator) { _str = str; _separator = separator; @@ -68,7 +68,7 @@ namespace MediaBrowser.Common.Extensions public ReadOnlySpan Current { get; private set; } - public readonly LineSplitEnumerator GetEnumerator() => this; + public readonly SplitEnumerator GetEnumerator() => this; public bool MoveNext() { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs index af6ec3245..c0f34afa7 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Server.Implementations.Tests.Data yield return new object[] { "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN", - new ItemImageInfo() + new ItemImageInfo { Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg", Type = ImageType.Primary, @@ -51,7 +51,27 @@ namespace Jellyfin.Server.Implementations.Tests.Data yield return new object[] { "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*0*0", - new ItemImageInfo() + new ItemImageInfo + { + Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg", + Type = ImageType.Primary, + } + }; + + yield return new object[] + { + "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary", + new ItemImageInfo + { + Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg", + Type = ImageType.Primary, + } + }; + + yield return new object[] + { + "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*600", + new ItemImageInfo { Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg", Type = ImageType.Primary, @@ -61,7 +81,7 @@ namespace Jellyfin.Server.Implementations.Tests.Data yield return new object[] { "%MetadataPath%/library/68/68578562b96c80a7ebd530848801f645/poster.jpg*637264380567586027*Primary*600*336", - new ItemImageInfo() + new ItemImageInfo { Path = "/meta/data/path/library/68/68578562b96c80a7ebd530848801f645/poster.jpg", Type = ImageType.Primary, @@ -76,7 +96,7 @@ namespace Jellyfin.Server.Implementations.Tests.Data [MemberData(nameof(ItemImageInfoFromValueString_Valid_TestData))] public void ItemImageInfoFromValueString_Valid_Success(string value, ItemImageInfo expected) { - var result = _sqliteItemRepository.ItemImageInfoFromValueString(value); + var result = _sqliteItemRepository.ItemImageInfoFromValueString(value.AsSpan()); Assert.Equal(expected.Path, result.Path); Assert.Equal(expected.Type, result.Type); Assert.Equal(expected.DateModified, result.DateModified); @@ -88,9 +108,10 @@ namespace Jellyfin.Server.Implementations.Tests.Data [Theory] [InlineData("")] [InlineData("*")] + [InlineData("https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0")] public void ItemImageInfoFromValueString_Invalid_Null(string value) { - Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value)); + Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value.AsSpan())); } public static IEnumerable DeserializeImages_Valid_TestData() From ee92dffa82bf2cc5eb6e8e088a391c3e22c03345 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 May 2021 09:01:23 +0000 Subject: [PATCH 376/402] Bump Swashbuckle.AspNetCore.ReDoc from 6.1.3 to 6.1.4 Bumps [Swashbuckle.AspNetCore.ReDoc](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) from 6.1.3 to 6.1.4. - [Release notes](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases) - [Commits](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/compare/v6.1.3...v6.1.4) Signed-off-by: dependabot[bot] --- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index d6d07cd27..c10c34b59 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -18,7 +18,7 @@ - + From ad3e835bcf7e800b483495af74adc36e10139be5 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 4 May 2021 19:57:03 +0200 Subject: [PATCH 377/402] remove redundant code --- MediaBrowser.Common/Extensions/SplitStringExtensions.cs | 7 ------- .../Data/SqliteItemRepositoryTests.cs | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/MediaBrowser.Common/Extensions/SplitStringExtensions.cs b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs index 6c1c45013..e78fa78d6 100644 --- a/MediaBrowser.Common/Extensions/SplitStringExtensions.cs +++ b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs @@ -86,13 +86,6 @@ namespace MediaBrowser.Common.Extensions return true; } - if (index < span.Length - 1 && span[index] == _separator) - { - Current = span.Slice(0, index); - _str = span[(index + 1)..]; - return true; - } - Current = span.Slice(0, index); _str = span[(index + 1)..]; return true; diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs index c0f34afa7..71f8c5181 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs @@ -96,7 +96,7 @@ namespace Jellyfin.Server.Implementations.Tests.Data [MemberData(nameof(ItemImageInfoFromValueString_Valid_TestData))] public void ItemImageInfoFromValueString_Valid_Success(string value, ItemImageInfo expected) { - var result = _sqliteItemRepository.ItemImageInfoFromValueString(value.AsSpan()); + var result = _sqliteItemRepository.ItemImageInfoFromValueString(value); Assert.Equal(expected.Path, result.Path); Assert.Equal(expected.Type, result.Type); Assert.Equal(expected.DateModified, result.DateModified); @@ -111,7 +111,7 @@ namespace Jellyfin.Server.Implementations.Tests.Data [InlineData("https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0")] public void ItemImageInfoFromValueString_Invalid_Null(string value) { - Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value.AsSpan())); + Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value)); } public static IEnumerable DeserializeImages_Valid_TestData() From 244ad5b22577efa4dc737db549e62e422f42449e Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Tue, 4 May 2021 22:57:27 +0200 Subject: [PATCH 378/402] Apply review feedback --- MediaBrowser.Model/Dlna/DeviceProfile.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 6ec4cb547..47d101613 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -432,8 +432,8 @@ namespace MediaBrowser.Model.Dlna /// The . public ResponseProfile? GetVideoMediaProfile( string container, - string audioCodec, - string videoCodec, + string? audioCodec, + string? videoCodec, int? width, int? height, int? bitDepth, From b2bb062ced57870eb0a116a9518685a8bae48131 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Tue, 4 May 2021 23:38:17 +0200 Subject: [PATCH 379/402] Revert shortened 'is ... or' check --- MediaBrowser.Model/Dlna/DirectPlayProfile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs index 417d915b5..7b8cbe181 100644 --- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs +++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs @@ -32,7 +32,7 @@ namespace MediaBrowser.Model.Dlna public bool SupportsAudioCodec(string codec) { - return (Type is DlnaProfileType.Audio or DlnaProfileType.Video) && ContainerProfile.ContainsContainer(AudioCodec, codec); + return (Type == DlnaProfileType.Audio || Type == DlnaProfileType.Video) && ContainerProfile.ContainsContainer(AudioCodec, codec); } } } From 031a5c122d6e874fab5f8e4c3b27de5c03ac7217 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sat, 1 May 2021 00:43:43 +0200 Subject: [PATCH 380/402] Improve documentation for DeviceProfile --- MediaBrowser.Model/Dlna/DeviceProfile.cs | 41 +++++++++++++----------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 47d101613..feb3d880e 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -8,13 +8,18 @@ using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna { /// - /// Defines the . + /// A represents a set of metadata which determines which content a certain device is able to play. + ///
+ /// Specifically, it defines the supported containers and + /// codecs (video and/or audio, including codec profiles and levels) + /// the device is able to direct play (without transcoding or remuxing), + /// as well as which containers/codecs to transcode to in case it isn't. ///
[XmlRoot("Profile")] public class DeviceProfile { /// - /// Gets or sets the Name. + /// Gets or sets the name of this device profile. /// public string? Name { get; set; } @@ -30,32 +35,32 @@ namespace MediaBrowser.Model.Dlna public DeviceIdentification? Identification { get; set; } /// - /// Gets or sets the FriendlyName. + /// Gets or sets the friendly name of the device profile, which can be shown to users. /// public string? FriendlyName { get; set; } /// - /// Gets or sets the Manufacturer. + /// Gets or sets the manufacturer of the device which this profile represents. /// public string? Manufacturer { get; set; } /// - /// Gets or sets the ManufacturerUrl. + /// Gets or sets an url for the manufacturer of the device which this profile represents. /// public string? ManufacturerUrl { get; set; } /// - /// Gets or sets the ModelName. + /// Gets or sets the model name of the device which this profile represents. /// public string? ModelName { get; set; } /// - /// Gets or sets the ModelDescription. + /// Gets or sets the model description of the device which this profile represents. /// public string? ModelDescription { get; set; } /// - /// Gets or sets the ModelNumber. + /// Gets or sets the model number of the device which this profile represents. /// public string? ModelNumber { get; set; } @@ -65,7 +70,7 @@ namespace MediaBrowser.Model.Dlna public string? ModelUrl { get; set; } /// - /// Gets or sets the SerialNumber. + /// Gets or sets the serial number of the device which this profile represents. /// public string? SerialNumber { get; set; } @@ -113,32 +118,32 @@ namespace MediaBrowser.Model.Dlna public int? MaxAlbumArtHeight { get; set; } /// - /// Gets or sets the MaxIconWidth. + /// Gets or sets the maximum allowed width of embedded icons. /// public int? MaxIconWidth { get; set; } /// - /// Gets or sets the MaxIconHeight. + /// Gets or sets the maximum allowed height of embedded icons. /// public int? MaxIconHeight { get; set; } /// - /// Gets or sets the MaxStreamingBitrate. + /// Gets or sets the maximum allowed bitrate for all streamed content. /// public int? MaxStreamingBitrate { get; set; } = 8000000; /// - /// Gets or sets the MaxStaticBitrate. + /// Gets or sets the maximum allowed bitrate for statically streamed content (= direct played files). /// public int? MaxStaticBitrate { get; set; } = 8000000; /// - /// Gets or sets the MusicStreamingTranscodingBitrate. + /// Gets or sets the maximum allowed bitrate for transcoded music streams. /// public int? MusicStreamingTranscodingBitrate { get; set; } = 128000; /// - /// Gets or sets the MaxStaticMusicBitrate. + /// Gets or sets the maximum allowed bitrate for statically streamed (= direct played) music files. /// public int? MaxStaticMusicBitrate { get; set; } = 8000000; @@ -198,12 +203,12 @@ namespace MediaBrowser.Model.Dlna public TranscodingProfile[] TranscodingProfiles { get; set; } = Array.Empty(); /// - /// Gets or sets the ContainerProfiles. + /// Gets or sets the container profiles. /// public ContainerProfile[] ContainerProfiles { get; set; } = Array.Empty(); /// - /// Gets or sets the CodecProfiles. + /// Gets or sets the codec profiles. /// public CodecProfile[] CodecProfiles { get; set; } = Array.Empty(); @@ -213,7 +218,7 @@ namespace MediaBrowser.Model.Dlna public ResponseProfile[] ResponseProfiles { get; set; } = Array.Empty(); /// - /// Gets or sets the SubtitleProfiles. + /// Gets or sets the subtitle profiles. /// public SubtitleProfile[] SubtitleProfiles { get; set; } = Array.Empty(); From 31cf9f4b76a258babec0838a6e114979dd23b93c Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 4 May 2021 18:21:08 -0600 Subject: [PATCH 381/402] Remove legacy apiclient generation --- .ci/azure-pipelines-api-client.yml | 59 ------------------- .../Properties/launchSettings.json | 3 +- apiclient/.openapi-generator-ignore | 2 - .../templates/typescript/axios/generate.sh | 11 ---- .../typescript/axios/package.mustache | 30 ---------- 5 files changed, 2 insertions(+), 103 deletions(-) delete mode 100644 .ci/azure-pipelines-api-client.yml delete mode 100644 apiclient/.openapi-generator-ignore delete mode 100644 apiclient/templates/typescript/axios/generate.sh delete mode 100644 apiclient/templates/typescript/axios/package.mustache diff --git a/.ci/azure-pipelines-api-client.yml b/.ci/azure-pipelines-api-client.yml deleted file mode 100644 index 0e944e6f4..000000000 --- a/.ci/azure-pipelines-api-client.yml +++ /dev/null @@ -1,59 +0,0 @@ -parameters: - - name: LinuxImage - type: string - default: "ubuntu-latest" - - name: GeneratorVersion - type: string - default: "5.0.1" - -jobs: -- job: GenerateApiClients - displayName: 'Generate Api Clients' - condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v') - dependsOn: Test - - pool: - vmImage: "${{ parameters.LinuxImage }}" - - steps: - - task: DownloadPipelineArtifact@2 - displayName: 'Download OpenAPI Spec Artifact' - inputs: - source: 'current' - artifact: "OpenAPI Spec" - path: "$(System.ArtifactsDirectory)/openapispec" - runVersion: "latest" - - - task: CmdLine@2 - displayName: 'Download OpenApi Generator' - inputs: - script: "wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/${{ parameters.GeneratorVersion }}/openapi-generator-cli-${{ parameters.GeneratorVersion }}.jar -O openapi-generator-cli.jar" - -## Authenticate with npm registry - - task: npmAuthenticate@0 - inputs: - workingFile: ./.npmrc - customEndpoint: 'jellyfin-bot for NPM' - -## Generate npm api client - - task: CmdLine@2 - displayName: 'Build stable typescript axios client' - inputs: - script: "bash ./apiclient/templates/typescript/axios/generate.sh $(System.ArtifactsDirectory)" - -## Run npm install - - task: Npm@1 - displayName: 'Install npm dependencies' - inputs: - command: install - workingDir: ./apiclient/generated/typescript/axios - -## Publish npm packages - - task: Npm@1 - displayName: 'Publish stable typescript axios client' - inputs: - command: custom - customCommand: publish --access public - publishRegistry: useExternalRegistry - publishEndpoint: 'jellyfin-bot for NPM' - workingDir: ./apiclient/generated/typescript/axios diff --git a/Jellyfin.Server/Properties/launchSettings.json b/Jellyfin.Server/Properties/launchSettings.json index 20d432afc..61a1723bd 100644 --- a/Jellyfin.Server/Properties/launchSettings.json +++ b/Jellyfin.Server/Properties/launchSettings.json @@ -6,7 +6,8 @@ "applicationUrl": "http://localhost:8096", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "commandLineArgs": "--webdir C:\\Users\\Cody\\Code\\Jellyfin\\jellyfin-web\\dist" }, "Jellyfin.Server (nowebclient)": { "commandName": "Project", diff --git a/apiclient/.openapi-generator-ignore b/apiclient/.openapi-generator-ignore deleted file mode 100644 index f3802cf54..000000000 --- a/apiclient/.openapi-generator-ignore +++ /dev/null @@ -1,2 +0,0 @@ -# Prevent generator from creating these files: -git_push.sh diff --git a/apiclient/templates/typescript/axios/generate.sh b/apiclient/templates/typescript/axios/generate.sh deleted file mode 100644 index 9599f85db..000000000 --- a/apiclient/templates/typescript/axios/generate.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -artifactsDirectory="${1}" - -java -jar openapi-generator-cli.jar generate \ - --input-spec ${artifactsDirectory}/openapispec/openapi.json \ - --generator-name typescript-axios \ - --output ./apiclient/generated/typescript/axios \ - --template-dir ./apiclient/templates/typescript/axios \ - --ignore-file-override ./apiclient/.openapi-generator-ignore \ - --additional-properties=useSingleRequestParameter="true",withSeparateModelsAndApi="true",modelPackage="models",apiPackage="api",npmName="axios" diff --git a/apiclient/templates/typescript/axios/package.mustache b/apiclient/templates/typescript/axios/package.mustache deleted file mode 100644 index 7bfab08cb..000000000 --- a/apiclient/templates/typescript/axios/package.mustache +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@jellyfin/client-axios", - "version": "10.7.0{{snapshotVersion}}", - "description": "Jellyfin api client using axios", - "author": "Jellyfin Contributors", - "keywords": [ - "axios", - "typescript", - "jellyfin" - ], - "license": "GPL-3.0-only", - "main": "./dist/index.js", - "typings": "./dist/index.d.ts", - "scripts": { - "build": "tsc --outDir dist/", - "prepublishOnly": "npm run build" - }, - "dependencies": { - "axios": "^0.19.2" - }, - "devDependencies": { - "@types/node": "^12.11.5", - "typescript": "^3.6.4" - }{{#npmRepository}},{{/npmRepository}} -{{#npmRepository}} - "publishConfig": { - "registry": "{{npmRepository}}" - } -{{/npmRepository}} -} From 1a178e84905c776766c49551f52dc163b93c81b0 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 4 May 2021 19:11:01 -0600 Subject: [PATCH 382/402] Remove Required attributes --- MediaBrowser.Model/Dlna/ContainerProfile.cs | 2 -- MediaBrowser.Model/Dlna/DirectPlayProfile.cs | 1 - MediaBrowser.Model/Dlna/TranscodingProfile.cs | 5 ----- 3 files changed, 8 deletions(-) diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs index 54d4f7f38..c66ec8bc3 100644 --- a/MediaBrowser.Model/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -9,13 +9,11 @@ namespace MediaBrowser.Model.Dlna { public class ContainerProfile { - [Required] [XmlAttribute("type")] public DlnaProfileType Type { get; set; } public ProfileCondition[]? Conditions { get; set; } = Array.Empty(); - [Required] [XmlAttribute("container")] public string Container { get; set; } = string.Empty; diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs index 7b8cbe181..fa3ad098f 100644 --- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs +++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs @@ -16,7 +16,6 @@ namespace MediaBrowser.Model.Dlna [XmlAttribute("videoCodec")] public string? VideoCodec { get; set; } - [Required] [XmlAttribute("type")] public DlnaProfileType Type { get; set; } diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index ea446b733..214578a85 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -8,23 +8,18 @@ namespace MediaBrowser.Model.Dlna { public class TranscodingProfile { - [Required] [XmlAttribute("container")] public string Container { get; set; } = string.Empty; - [Required] [XmlAttribute("type")] public DlnaProfileType Type { get; set; } - [Required] [XmlAttribute("videoCodec")] public string VideoCodec { get; set; } = string.Empty; - [Required] [XmlAttribute("audioCodec")] public string AudioCodec { get; set; } = string.Empty; - [Required] [XmlAttribute("protocol")] public string Protocol { get; set; } = string.Empty; From 08d07656237fd53668796e0d6a2d78bbba5f706f Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 4 May 2021 19:17:16 -0600 Subject: [PATCH 383/402] Restore launchSettings.json --- Jellyfin.Server/Properties/launchSettings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jellyfin.Server/Properties/launchSettings.json b/Jellyfin.Server/Properties/launchSettings.json index 61a1723bd..20d432afc 100644 --- a/Jellyfin.Server/Properties/launchSettings.json +++ b/Jellyfin.Server/Properties/launchSettings.json @@ -6,8 +6,7 @@ "applicationUrl": "http://localhost:8096", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - "commandLineArgs": "--webdir C:\\Users\\Cody\\Code\\Jellyfin\\jellyfin-web\\dist" + } }, "Jellyfin.Server (nowebclient)": { "commandName": "Project", From 08213f2368aed728e2e12e92e5d799952f7b412a Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 4 May 2021 19:30:51 -0600 Subject: [PATCH 384/402] Fully remove reference. --- .ci/azure-pipelines.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 6430503f9..c028b6e3e 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -61,6 +61,3 @@ jobs: - ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}: - template: azure-pipelines-package.yml - -- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}: - - template: azure-pipelines-api-client.yml From e57b80b8e2c094c27eef835a335d4c7386bff968 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Wed, 5 May 2021 10:10:51 +0200 Subject: [PATCH 385/402] Add support for fanart aspect in thumb tag --- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 1 + .../Parsers/MovieNfoParserTests.cs | 6 +++++- .../Test Data/Justice League.nfo | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 317dc0bf6..302c93f0b 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -1333,6 +1333,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers "discart" => ImageType.Disc, "landscape" => ImageType.Thumb, "clearart" => ImageType.Art, + "fanart" => ImageType.Backdrop, // unknown type (including "poster") --> primary _ => ImageType.Primary, }; diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index b58151b3b..30a48857a 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -156,7 +156,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal("Justice League Collection", item.CollectionName); // Images - Assert.Equal(6, result.RemoteImages.Count); + Assert.Equal(7, result.RemoteImages.Count); var posters = result.RemoteImages.Where(x => x.type == ImageType.Primary).ToList(); Assert.Single(posters); @@ -182,6 +182,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Single(discArt); Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png", discArt[0].url); + var backdrop = result.RemoteImages.Where(x => x.type == ImageType.Backdrop).ToList(); + Assert.Single(backdrop); + Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg", backdrop[0].url); + // Local Image - contains only one item depending on operating system Assert.Single(result.Images); Assert.Equal(_localImageFileMetadata.Name, result.Images[0].FileInfo.Name); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo index b0c5e3c57..4e8c79dca 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo +++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Justice League.nfo @@ -82,8 +82,8 @@ https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a0b913c233be.png https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-59dc595362ef1.png + https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg - https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg From 65a9a4771afc5b6b7c956f7b799586400b23af8b Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Wed, 5 May 2021 12:25:54 +0200 Subject: [PATCH 386/402] Fix direct play for DirectPlayProfiles without any codecs set 70771fdcd60ec5d8a9f13713662778c7e57d0633 broke direct play by treating empty container/codec strings as unsupported in `ContainerProfile.ContainsContainer()`` (which is also used for video and audio codec checks). Instead, they should be treated as supported, for both the positive and negative list option. --- MediaBrowser.Model/Dlna/ContainerProfile.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs index c66ec8bc3..b059e43f3 100644 --- a/MediaBrowser.Model/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -55,7 +55,8 @@ namespace MediaBrowser.Model.Dlna { if (profileContainers == null || profileContainers.Length == 0) { - return isNegativeList; + // Empty profiles always support all containers/codecs + return true; } var allInputContainers = SplitValue(inputContainer); From 91c2a57b284011a21c926e9238dbc7edb5eaab13 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 12:57:01 +0200 Subject: [PATCH 387/402] Enable nullable reference types for MediaBrowser.Common --- Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 5 +++-- .../Controllers/ConfigurationController.cs | 6 ++++++ .../Configuration/ConfigurationStore.cs | 2 ++ .../ConfigurationUpdateEventArgs.cs | 1 + .../Configuration/IApplicationPaths.cs | 2 ++ .../Cryptography/PasswordHash.cs | 1 - MediaBrowser.Common/Events/EventHelper.cs | 4 ++-- .../Extensions/BaseExtensions.cs | 2 -- .../Extensions/CopyToExtensions.cs | 2 -- .../Extensions/HttpContextExtensions.cs | 2 +- .../Extensions/MethodNotAllowedException.cs | 2 -- .../Extensions/ProcessExtensions.cs | 2 -- .../Extensions/RateLimitExceededException.cs | 1 - .../Extensions/ResourceNotFoundException.cs | 2 -- .../Extensions/ShuffleExtensions.cs | 2 -- .../Extensions/SplitStringExtensions.cs | 2 +- .../Extensions/StreamExtensions.cs | 2 -- MediaBrowser.Common/IApplicationHost.cs | 2 ++ .../JsonCommaDelimitedArrayConverter.cs | 14 ++++++++++---- .../JsonCommaDelimitedArrayConverterFactory.cs | 4 ++-- .../JsonNullableStructConverterFactory.cs | 6 +++--- .../JsonOmdbNotAvailableStringConverter.cs | 18 ++++++++++++------ .../JsonPipeDelimitedArrayConverter.cs | 13 ++++++++++--- .../JsonPipeDelimitedArrayConverterFactory.cs | 4 ++-- .../Json/Converters/JsonStringConverter.cs | 8 ++++---- .../Json/Converters/JsonVersionConverter.cs | 2 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 1 + MediaBrowser.Common/Net/INetworkManager.cs | 1 - MediaBrowser.Common/Net/IPHost.cs | 1 - MediaBrowser.Common/Net/IPNetAddress.cs | 1 - MediaBrowser.Common/Net/IPObject.cs | 1 - MediaBrowser.Common/Plugins/BasePlugin.cs | 2 ++ MediaBrowser.Common/Plugins/BasePluginOfT.cs | 2 ++ MediaBrowser.Common/Plugins/IPlugin.cs | 2 ++ MediaBrowser.Common/Plugins/IPluginManager.cs | 2 -- MediaBrowser.Common/Plugins/LocalPlugin.cs | 1 - MediaBrowser.Common/Plugins/PluginManifest.cs | 2 -- .../Progress/ActionableProgress.cs | 4 ++-- MediaBrowser.Common/Progress/SimpleProgress.cs | 2 +- .../Providers/ProviderIdParsers.cs | 4 +--- .../Updates/IInstallationManager.cs | 2 -- .../Updates/InstallationEventArgs.cs | 2 ++ .../Updates/InstallationFailedEventArgs.cs | 1 + 43 files changed, 78 insertions(+), 64 deletions(-) diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs index 7d68aecf9..392498c53 100644 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs @@ -77,8 +77,9 @@ namespace Jellyfin.Api.Auth return false; } - var ip = _httpContextAccessor.HttpContext.GetNormalizedRemoteIp(); - var isInLocalNetwork = _networkManager.IsInLocalNetwork(ip); + var isInLocalNetwork = _httpContextAccessor.HttpContext != null + && _networkManager.IsInLocalNetwork(_httpContextAccessor.HttpContext.GetNormalizedRemoteIp()); + // User cannot access remotely and user is remote if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !isInLocalNetwork) { diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index 049a4bed7..b6309baab 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -1,3 +1,4 @@ +using System; using System.ComponentModel.DataAnnotations; using System.Net.Mime; using System.Text.Json; @@ -94,6 +95,11 @@ namespace Jellyfin.Api.Controllers { var configurationType = _configurationManager.GetConfigurationType(key); var configuration = await JsonSerializer.DeserializeAsync(Request.Body, configurationType, _serializerOptions).ConfigureAwait(false); + if (configuration == null) + { + throw new ArgumentException("Body doesn't contain a valid configuration"); + } + _configurationManager.SaveConfiguration(key, configuration); return NoContent(); } diff --git a/MediaBrowser.Common/Configuration/ConfigurationStore.cs b/MediaBrowser.Common/Configuration/ConfigurationStore.cs index d31d45e4c..050ab1ab5 100644 --- a/MediaBrowser.Common/Configuration/ConfigurationStore.cs +++ b/MediaBrowser.Common/Configuration/ConfigurationStore.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; namespace MediaBrowser.Common.Configuration diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs index 344aecf53..2df87d879 100644 --- a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs +++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs index 57c654667..1370e6d79 100644 --- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -1,3 +1,5 @@ +#nullable disable + namespace MediaBrowser.Common.Configuration { /// diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs index ec21d0580..0e2065302 100644 --- a/MediaBrowser.Common/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs @@ -1,5 +1,4 @@ #pragma warning disable CS1591 -#nullable enable using System; using System.Collections.Generic; diff --git a/MediaBrowser.Common/Events/EventHelper.cs b/MediaBrowser.Common/Events/EventHelper.cs index c9d3226ac..a9cf86fbc 100644 --- a/MediaBrowser.Common/Events/EventHelper.cs +++ b/MediaBrowser.Common/Events/EventHelper.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Common.Events /// The sender. /// The instance containing the event data. /// The logger. - public static void QueueEventIfNotNull(EventHandler handler, object sender, EventArgs args, ILogger logger) + public static void QueueEventIfNotNull(EventHandler? handler, object sender, EventArgs args, ILogger logger) { if (handler != null) { @@ -43,7 +43,7 @@ namespace MediaBrowser.Common.Events /// The sender. /// The args. /// The logger. - public static void QueueEventIfNotNull(EventHandler handler, object sender, T args, ILogger logger) + public static void QueueEventIfNotNull(EventHandler? handler, object sender, T args, ILogger logger) { if (handler != null) { diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index 40020093b..08964420e 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Security.Cryptography; using System.Text; diff --git a/MediaBrowser.Common/Extensions/CopyToExtensions.cs b/MediaBrowser.Common/Extensions/CopyToExtensions.cs index 94bf7c740..2ecbc6539 100644 --- a/MediaBrowser.Common/Extensions/CopyToExtensions.cs +++ b/MediaBrowser.Common/Extensions/CopyToExtensions.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Collections.Generic; namespace MediaBrowser.Common.Extensions diff --git a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs index e51ad42d1..1e5877c84 100644 --- a/MediaBrowser.Common/Extensions/HttpContextExtensions.cs +++ b/MediaBrowser.Common/Extensions/HttpContextExtensions.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Common.Extensions { return (context.Connection.LocalIpAddress == null && context.Connection.RemoteIpAddress == null) - || context.Connection.LocalIpAddress.Equals(context.Connection.RemoteIpAddress); + || Equals(context.Connection.LocalIpAddress, context.Connection.RemoteIpAddress); } /// diff --git a/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs index 258bd6662..48e758ee4 100644 --- a/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs +++ b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; namespace MediaBrowser.Common.Extensions diff --git a/MediaBrowser.Common/Extensions/ProcessExtensions.cs b/MediaBrowser.Common/Extensions/ProcessExtensions.cs index 2f52ba196..c74787122 100644 --- a/MediaBrowser.Common/Extensions/ProcessExtensions.cs +++ b/MediaBrowser.Common/Extensions/ProcessExtensions.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Diagnostics; using System.Threading; diff --git a/MediaBrowser.Common/Extensions/RateLimitExceededException.cs b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs index 7c7bdaa92..95802a462 100644 --- a/MediaBrowser.Common/Extensions/RateLimitExceededException.cs +++ b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs index ebac9d8e6..22130c5a1 100644 --- a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs +++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; namespace MediaBrowser.Common.Extensions diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs index 6f0ea9bd5..2604abf85 100644 --- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs +++ b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Collections.Generic; diff --git a/MediaBrowser.Common/Extensions/SplitStringExtensions.cs b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs index e78fa78d6..9c9108495 100644 --- a/MediaBrowser.Common/Extensions/SplitStringExtensions.cs +++ b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs @@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#nullable enable + #pragma warning disable CS1591 #pragma warning disable CA1034 using System; diff --git a/MediaBrowser.Common/Extensions/StreamExtensions.cs b/MediaBrowser.Common/Extensions/StreamExtensions.cs index cd77be7b2..08d81ffc3 100644 --- a/MediaBrowser.Common/Extensions/StreamExtensions.cs +++ b/MediaBrowser.Common/Extensions/StreamExtensions.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index c3e4ed6db..46d93e494 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Reflection; diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs index 2ec702165..55c4665e8 100644 --- a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Common.Json.Converters /// Convert comma delimited string to array of type. /// /// Type to convert to. - public class JsonCommaDelimitedArrayConverter : JsonConverter + public class JsonCommaDelimitedArrayConverter : JsonConverter { private readonly TypeConverter _typeConverter; @@ -22,11 +22,17 @@ namespace MediaBrowser.Common.Json.Converters } /// - public override T[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + if (reader.TokenType == JsonTokenType.String) { - var stringEntries = reader.GetString().Split(',', StringSplitOptions.RemoveEmptyEntries); + // GetString can't return null here because we already handled it above + var stringEntries = reader.GetString()!.Split(',', StringSplitOptions.RemoveEmptyEntries); if (stringEntries.Length == 0) { return Array.Empty(); @@ -67,7 +73,7 @@ namespace MediaBrowser.Common.Json.Converters } /// - public override void Write(Utf8JsonWriter writer, T[] value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, T[]? value, JsonSerializerOptions options) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs index 24ed3ea19..de41348dd 100644 --- a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs +++ b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs @@ -19,10 +19,10 @@ namespace MediaBrowser.Common.Json.Converters } /// - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var structType = typeToConvert.GetElementType() ?? typeToConvert.GenericTypeArguments[0]; - return (JsonConverter)Activator.CreateInstance(typeof(JsonCommaDelimitedArrayConverter<>).MakeGenericType(structType)); + return (JsonConverter?)Activator.CreateInstance(typeof(JsonCommaDelimitedArrayConverter<>).MakeGenericType(structType)); } } } diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs index d5b54e3ca..e2a3d798a 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs @@ -18,10 +18,10 @@ namespace MediaBrowser.Common.Json.Converters } /// - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var structType = typeToConvert.GenericTypeArguments[0]; - return (JsonConverter)Activator.CreateInstance(typeof(JsonNullableStructConverter<>).MakeGenericType(structType)); + return (JsonConverter?)Activator.CreateInstance(typeof(JsonNullableStructConverter<>).MakeGenericType(structType)); } } -} \ No newline at end of file +} diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs index 6a8790374..77cf46b70 100644 --- a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs @@ -7,15 +7,21 @@ namespace MediaBrowser.Common.Json.Converters /// /// Converts a string N/A to string.Empty. /// - public class JsonOmdbNotAvailableStringConverter : JsonConverter + public class JsonOmdbNotAvailableStringConverter : JsonConverter { /// - public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + if (reader.TokenType == JsonTokenType.String) { - var str = reader.GetString(); - if (str != null && str.Equals("N/A", StringComparison.OrdinalIgnoreCase)) + // GetString can't return null here because we already handled it above + var str = reader.GetString()!; + if (str.Equals("N/A", StringComparison.OrdinalIgnoreCase)) { return null; } @@ -23,11 +29,11 @@ namespace MediaBrowser.Common.Json.Converters return str; } - return JsonSerializer.Deserialize(ref reader, options); + return JsonSerializer.Deserialize(ref reader, options); } /// - public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) { writer.WriteStringValue(value); } diff --git a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs index c408a3be1..c83657b5f 100644 --- a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs @@ -24,10 +24,16 @@ namespace MediaBrowser.Common.Json.Converters /// public override T[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + { + return Array.Empty(); + } + if (reader.TokenType == JsonTokenType.String) { - var stringEntries = reader.GetString()?.Split('|', StringSplitOptions.RemoveEmptyEntries); - if (stringEntries == null || stringEntries.Length == 0) + // GetString can't return null here because we already handled it above + var stringEntries = reader.GetString()!.Split('|', StringSplitOptions.RemoveEmptyEntries); + if (stringEntries.Length == 0) { return Array.Empty(); } @@ -63,7 +69,8 @@ namespace MediaBrowser.Common.Json.Converters return typedValues; } - return JsonSerializer.Deserialize(ref reader, options); + // can't return null here because we already handled it above + return JsonSerializer.Deserialize(ref reader, options)!; } /// diff --git a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs index 5e77223ef..1bebc49ec 100644 --- a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs +++ b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs @@ -19,10 +19,10 @@ namespace MediaBrowser.Common.Json.Converters } /// - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var structType = typeToConvert.GetElementType() ?? typeToConvert.GenericTypeArguments[0]; - return (JsonConverter)Activator.CreateInstance(typeof(JsonPipeDelimitedArrayConverter<>).MakeGenericType(structType)); + return (JsonConverter?)Activator.CreateInstance(typeof(JsonPipeDelimitedArrayConverter<>).MakeGenericType(structType)); } } } diff --git a/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs index 669b3cd07..6cd980e48 100644 --- a/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs @@ -9,10 +9,10 @@ namespace MediaBrowser.Common.Json.Converters /// /// Converter to allow the serializer to read strings. /// - public class JsonStringConverter : JsonConverter + public class JsonStringConverter : JsonConverter { /// - public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch { @@ -23,7 +23,7 @@ namespace MediaBrowser.Common.Json.Converters } /// - public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) { writer.WriteStringValue(value); } @@ -36,4 +36,4 @@ namespace MediaBrowser.Common.Json.Converters return Encoding.UTF8.GetString(utf8Bytes); } } -} \ No newline at end of file +} diff --git a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs index f69e868cc..81c093c54 100644 --- a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Common.Json.Converters { /// public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => new Version(reader.GetString()); + => new Version(reader.GetString()!); // Will throw ArgumentNullException on null /// public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 0d9f78704..0299a8456 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -33,6 +33,7 @@ false true true + enable AllEnabledByDefault ../jellyfin.ruleset true diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 185df5b77..b93939730 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index fb3ef9b12..72197db07 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.Diagnostics; using System.Linq; diff --git a/MediaBrowser.Common/Net/IPNetAddress.cs b/MediaBrowser.Common/Net/IPNetAddress.cs index 589aad4b0..f6e3971bf 100644 --- a/MediaBrowser.Common/Net/IPNetAddress.cs +++ b/MediaBrowser.Common/Net/IPNetAddress.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.Net; using System.Net.Sockets; diff --git a/MediaBrowser.Common/Net/IPObject.cs b/MediaBrowser.Common/Net/IPObject.cs index 3542dcd75..2612268fd 100644 --- a/MediaBrowser.Common/Net/IPObject.cs +++ b/MediaBrowser.Common/Net/IPObject.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.Net; using System.Net.Sockets; diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index ad5a7338d..8972089a8 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.IO; using System.Reflection; diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs index 99c226f50..361f4803b 100644 --- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs +++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs @@ -1,4 +1,6 @@ +#nullable disable #pragma warning disable SA1649 // File name should match first type name + using System; using System.IO; using System.Runtime.InteropServices; diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs index b2ba1179c..01e0a536d 100644 --- a/MediaBrowser.Common/Plugins/IPlugin.cs +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using MediaBrowser.Model.Plugins; diff --git a/MediaBrowser.Common/Plugins/IPluginManager.cs b/MediaBrowser.Common/Plugins/IPluginManager.cs index 0e2e814cb..176bcbbd5 100644 --- a/MediaBrowser.Common/Plugins/IPluginManager.cs +++ b/MediaBrowser.Common/Plugins/IPluginManager.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Collections.Generic; using System.Reflection; diff --git a/MediaBrowser.Common/Plugins/LocalPlugin.cs b/MediaBrowser.Common/Plugins/LocalPlugin.cs index 12a1ad1ec..4c8e2d504 100644 --- a/MediaBrowser.Common/Plugins/LocalPlugin.cs +++ b/MediaBrowser.Common/Plugins/LocalPlugin.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.Collections.Generic; using MediaBrowser.Model.Plugins; diff --git a/MediaBrowser.Common/Plugins/PluginManifest.cs b/MediaBrowser.Common/Plugins/PluginManifest.cs index 4c724f694..2910dbe14 100644 --- a/MediaBrowser.Common/Plugins/PluginManifest.cs +++ b/MediaBrowser.Common/Plugins/PluginManifest.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Text.Json.Serialization; using MediaBrowser.Model.Plugins; diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs index fe7cb1078..0ba46ea3b 100644 --- a/MediaBrowser.Common/Progress/ActionableProgress.cs +++ b/MediaBrowser.Common/Progress/ActionableProgress.cs @@ -14,9 +14,9 @@ namespace MediaBrowser.Common.Progress /// /// The _actions. /// - private Action _action; + private Action? _action; - public event EventHandler ProgressChanged; + public event EventHandler? ProgressChanged; /// /// Registers the action. diff --git a/MediaBrowser.Common/Progress/SimpleProgress.cs b/MediaBrowser.Common/Progress/SimpleProgress.cs index 988d8ad34..7071f2bc3 100644 --- a/MediaBrowser.Common/Progress/SimpleProgress.cs +++ b/MediaBrowser.Common/Progress/SimpleProgress.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Common.Progress { public class SimpleProgress : IProgress { - public event EventHandler ProgressChanged; + public event EventHandler? ProgressChanged; public void Report(T value) { diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs index 64c2e1976..33d09ed38 100644 --- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -1,6 +1,4 @@ -#nullable enable - -using System; +using System; using System.Diagnostics.CodeAnalysis; namespace MediaBrowser.Common.Providers diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index 0844c2d79..c2a28e0a2 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs index adf336313..f4f759955 100644 --- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs +++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using MediaBrowser.Model.Updates; diff --git a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs index 46f10c84f..d37146195 100644 --- a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs +++ b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs @@ -1,3 +1,4 @@ +#nullable disable #pragma warning disable CS1591 using System; From 39931fe3ade3ad10e758b3dbb5acf80c37bc05fa Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 13:33:34 +0200 Subject: [PATCH 388/402] Add regression test for ContainerProfile.ContainsContainer --- MediaBrowser.Model/Dlna/ContainerProfile.cs | 1 - .../Dlna/ContainerProfileTests.cs | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs index c66ec8bc3..530ffed43 100644 --- a/MediaBrowser.Model/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 using System; -using System.ComponentModel.DataAnnotations; using System.Linq; using System.Xml.Serialization; diff --git a/tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs b/tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs new file mode 100644 index 000000000..cca056c28 --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Dlna/ContainerProfileTests.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Model.Dlna; +using Xunit; + +namespace Jellyfin.Model.Tests.Dlna +{ + public class ContainerProfileTests + { + private readonly ContainerProfile _emptyContainerProfile = new ContainerProfile(); + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("mp4")] + public void ContainsContainer_EmptyContainerProfile_True(string? containers) + { + Assert.True(_emptyContainerProfile.ContainsContainer(containers)); + } + } +} From f2c10471bf00263adc6411b38db60ba931d0ec15 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 5 May 2021 12:37:36 +0100 Subject: [PATCH 389/402] Code Clean up: Use Pattern Matching (#5838) Co-authored-by: Cody Robibero Co-authored-by: Patrick Barron <18354464+barronpm@users.noreply.github.com> --- .../Images/PlaylistImageProvider.cs | 4 +--- .../Library/MusicManager.cs | 3 +-- .../ScheduledTasks/ScheduledTaskWorker.cs | 4 +--- .../TV/TVSeriesManager.cs | 8 ++------ .../Entities/CollectionFolder.cs | 4 +--- .../Entities/Movies/BoxSet.cs | 3 +-- MediaBrowser.Controller/Entities/Photo.cs | 3 +-- MediaBrowser.Controller/Entities/TV/Series.cs | 11 ++--------- MediaBrowser.Controller/Providers/ItemInfo.cs | 3 +-- .../Parsers/BaseItemXmlParser.cs | 3 +-- .../Savers/BaseXmlSaver.cs | 6 ++---- .../BdInfo/BdInfoExaminer.cs | 16 ++++------------ .../Manager/MetadataService.cs | 6 ++---- .../MediaInfo/AudioImageProvider.cs | 4 +--- .../MediaInfo/SubtitleDownloader.cs | 4 +--- .../MediaInfo/VideoImageProvider.cs | 4 +--- 16 files changed, 23 insertions(+), 63 deletions(-) diff --git a/Emby.Server.Implementations/Images/PlaylistImageProvider.cs b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs index 0ce1b91e8..a4c106e87 100644 --- a/Emby.Server.Implementations/Images/PlaylistImageProvider.cs +++ b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs @@ -29,9 +29,7 @@ namespace Emby.Server.Implementations.Images { var subItem = i.Item2; - var episode = subItem as Episode; - - if (episode != null) + if (subItem is Episode episode) { var series = episode.Series; if (series != null && series.HasImage(ImageType.Primary)) diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index 658c53f28..f8bae4fd1 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -100,8 +100,7 @@ namespace Emby.Server.Implementations.Library public List GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions) { - var genre = item as MusicGenre; - if (genre != null) + if (item is MusicGenre genre) { return GetInstantMixFromGenreIds(new[] { item.Id }, user, dtoOptions); } diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 9c0e92705..61dccaa19 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -348,9 +348,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { var trigger = (ITaskTrigger)sender; - var configurableTask = ScheduledTask as IConfigurableScheduledTask; - - if (configurableTask != null && !configurableTask.IsEnabled) + if (ScheduledTask is IConfigurableScheduledTask configurableTask && !configurableTask.IsEnabled) { return; } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index d3f6fa34d..829df64bf 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -43,9 +43,7 @@ namespace Emby.Server.Implementations.TV string presentationUniqueKey = null; if (!string.IsNullOrEmpty(request.SeriesId)) { - var series = _libraryManager.GetItemById(request.SeriesId) as Series; - - if (series != null) + if (_libraryManager.GetItemById(request.SeriesId) is Series series) { presentationUniqueKey = GetUniqueSeriesKey(series); } @@ -95,9 +93,7 @@ namespace Emby.Server.Implementations.TV int? limit = null; if (!string.IsNullOrEmpty(request.SeriesId)) { - var series = _libraryManager.GetItemById(request.SeriesId) as Series; - - if (series != null) + if (_libraryManager.GetItemById(request.SeriesId) is Series series) { presentationUniqueKey = GetUniqueSeriesKey(series); limit = 1; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 16a2c77e9..347d5b73c 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -354,9 +354,7 @@ namespace MediaBrowser.Controller.Entities if (result.Count == 0) { - var folder = LibraryManager.FindByPath(path, true) as Folder; - - if (folder != null) + if (LibraryManager.FindByPath(path, true) is Folder folder) { result.Add(folder); } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 05e4229ca..507f400f1 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -217,8 +217,7 @@ namespace MediaBrowser.Controller.Entities.Movies private IEnumerable FlattenItems(BaseItem item, List expandedFolders) { - var boxset = item as BoxSet; - if (boxset != null) + if (item is BoxSet boxset) { if (!expandedFolders.Contains(item.Id)) { diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 2fc66176f..0f82f742f 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -24,8 +24,7 @@ namespace MediaBrowser.Controller.Entities var parents = GetParents(); foreach (var parent in parents) { - var photoAlbum = parent as PhotoAlbum; - if (photoAlbum != null) + if (parent is PhotoAlbum photoAlbum) { return photoAlbum; } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 9f9a2ad50..06a405121 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -316,20 +316,13 @@ namespace MediaBrowser.Controller.Entities.TV cancellationToken.ThrowIfCancellationRequested(); - var skipItem = false; - - var episode = item as Episode; - - if (episode != null + bool skipItem = item is Episode episode && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh && !refreshOptions.ReplaceAllMetadata && episode.IsMissingEpisode && episode.LocationType == LocationType.Virtual && episode.PremiereDate.HasValue - && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30) - { - skipItem = true; - } + && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30; if (!skipItem) { diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs index b50def043..3a97127ea 100644 --- a/MediaBrowser.Controller/Providers/ItemInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemInfo.cs @@ -14,8 +14,7 @@ namespace MediaBrowser.Controller.Providers ContainingFolderPath = item.ContainingFolderPath; IsInMixedFolder = item.IsInMixedFolder; - var video = item as Video; - if (video != null) + if (item is Video video) { VideoType = video.VideoType; IsPlaceHolder = video.IsPlaceHolder; diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index 5f620634f..32e5ac761 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -468,8 +468,7 @@ namespace MediaBrowser.LocalMetadata.Parsers { var val = reader.ReadElementContentAsString(); - var hasDisplayOrder = item as IHasDisplayOrder; - if (hasDisplayOrder != null) + if (item is IHasDisplayOrder hasDisplayOrder) { if (!string.IsNullOrWhiteSpace(val)) { diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index dfbce5f49..98ed3dcf7 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -296,8 +296,7 @@ namespace MediaBrowser.LocalMetadata.Savers writer.WriteEndElement(); } - var hasDisplayOrder = item as IHasDisplayOrder; - if (hasDisplayOrder != null && !string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder)) + if (item is IHasDisplayOrder hasDisplayOrder && !string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder)) { writer.WriteElementString("DisplayOrder", hasDisplayOrder.DisplayOrder); } @@ -312,8 +311,7 @@ namespace MediaBrowser.LocalMetadata.Savers writer.WriteElementString("ProductionYear", item.ProductionYear.Value.ToString(_usCulture)); } - var hasAspectRatio = item as IHasAspectRatio; - if (hasAspectRatio != null) + if (item is IHasAspectRatio hasAspectRatio) { if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio)) { diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index 9108d9649..6ebaa4fff 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -61,33 +61,25 @@ namespace MediaBrowser.MediaEncoding.BdInfo foreach (var stream in playlist.SortedStreams) { - var videoStream = stream as TSVideoStream; - - if (videoStream != null) + if (stream is TSVideoStream videoStream) { AddVideoStream(mediaStreams, videoStream); continue; } - var audioStream = stream as TSAudioStream; - - if (audioStream != null) + if (stream is TSAudioStream audioStream) { AddAudioStream(mediaStreams, audioStream); continue; } - var textStream = stream as TSTextStream; - - if (textStream != null) + if (stream is TSTextStream textStream) { AddSubtitleStream(mediaStreams, textStream); continue; } - var graphicsStream = stream as TSGraphicsStream; - - if (graphicsStream != null) + if (stream is TSGraphicsStream graphicsStream) { AddSubtitleStream(mediaStreams, graphicsStream); } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 6b778a090..401c7e99f 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -281,8 +281,7 @@ namespace MediaBrowser.Providers.Manager return true; } - var folder = item as Folder; - if (folder != null) + if (item is Folder folder) { return folder.SupportsDateLastMediaAdded || folder.SupportsCumulativeRunTimeTicks; } @@ -336,8 +335,7 @@ namespace MediaBrowser.Providers.Manager private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IList children) { - var folder = item as Folder; - if (folder != null && folder.SupportsCumulativeRunTimeTicks) + if (item is Folder folder && folder.SupportsCumulativeRunTimeTicks) { long ticks = 0; diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 64ad1bddf..03e45fb86 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -137,9 +137,7 @@ namespace MediaBrowser.Providers.MediaInfo return false; } - var audio = item as Audio; - - return audio != null; + return item is Audio; } } } diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs index 912aedb0d..44ab5aa5b 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs @@ -172,9 +172,7 @@ namespace MediaBrowser.Providers.MediaInfo SubtitleFetcherOrder = subtitleFetcherOrder }; - var episode = video as Episode; - - if (episode != null) + if (video is Episode episode) { request.IndexNumberEnd = episode.IndexNumberEnd; request.SeriesName = episode.SeriesName; diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index c36c3af6a..30af6710a 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -154,9 +154,7 @@ namespace MediaBrowser.Providers.MediaInfo return false; } - var video = item as Video; - - if (video != null && !video.IsPlaceHolder && video.IsCompleteMedia) + if (item is Video video && !video.IsPlaceHolder && video.IsCompleteMedia) { return true; } From 2e98de90628e9a4e42fb182f2d5a2a296acfd827 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 5 May 2021 12:51:14 +0100 Subject: [PATCH 390/402] Code Clean up: Convert to null-coalescing operator ?? (#5845) Co-authored-by: Cody Robibero Co-authored-by: Patrick Barron <18354464+barronpm@users.noreply.github.com> --- .../ApplicationHost.cs | 10 +---- Emby.Server.Implementations/Dto/DtoService.cs | 15 ++----- .../Library/LibraryManager.cs | 5 +-- .../Library/Resolvers/TV/EpisodeResolver.cs | 14 ++---- .../LiveTv/EmbyTV/EmbyTV.cs | 10 ++--- .../LiveTv/Listings/SchedulesDirect.cs | 7 +-- .../LiveTv/LiveTvManager.cs | 15 ++----- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 5 +-- .../Plugins/PluginManager.cs | 13 +----- .../ScheduledTasks/ScheduledTaskWorker.cs | 7 +-- .../Session/SessionManager.cs | 5 +-- Jellyfin.Api/Controllers/PluginsController.cs | 7 +-- Jellyfin.Api/Controllers/SearchController.cs | 5 +-- Jellyfin.Api/Helpers/StreamingHelpers.cs | 5 +-- MediaBrowser.Common/Net/IPHost.cs | 5 +-- MediaBrowser.Common/Plugins/BasePluginOfT.cs | 5 +-- MediaBrowser.Controller/Entities/BaseItem.cs | 26 ++++------- MediaBrowser.Controller/Entities/UserView.cs | 5 +-- .../Library/ItemResolveArgs.cs | 5 +-- MediaBrowser.Controller/Playlists/Playlist.cs | 5 +-- .../Providers/MetadataRefreshOptions.cs | 5 +-- .../Providers/MetadataResult.cs | 16 +++---- .../Probing/ProbeResultNormalizer.cs | 45 +++++-------------- .../Entities/ProviderIdsExtensions.cs | 5 +-- .../MediaInfo/FFProbeVideoInfo.cs | 5 +-- .../Subtitles/SubtitleManager.cs | 5 +-- RSSDP/SsdpCommunicationsServer.cs | 5 +-- 27 files changed, 60 insertions(+), 200 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 703f8d20d..75d8fc113 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -335,10 +335,7 @@ namespace Emby.Server.Implementations { get { - if (_deviceId == null) - { - _deviceId = new DeviceId(ApplicationPaths, LoggerFactory); - } + _deviceId ??= new DeviceId(ApplicationPaths, LoggerFactory); return _deviceId.Value; } @@ -370,10 +367,7 @@ namespace Emby.Server.Implementations /// System.Object. protected object CreateInstanceSafe(Type type) { - if (_creatingInstances == null) - { - _creatingInstances = new List(); - } + _creatingInstances ??= new List(); if (_creatingInstances.IndexOf(type) != -1) { diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 54b18a8c8..4ae35039a 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -665,10 +665,7 @@ namespace Emby.Server.Implementations.Dto var tag = GetImageCacheTag(item, image); if (!string.IsNullOrEmpty(image.BlurHash)) { - if (dto.ImageBlurHashes == null) - { - dto.ImageBlurHashes = new Dictionary>(); - } + dto.ImageBlurHashes ??= new Dictionary>(); if (!dto.ImageBlurHashes.ContainsKey(image.Type)) { @@ -702,10 +699,7 @@ namespace Emby.Server.Implementations.Dto if (hashes.Count > 0) { - if (dto.ImageBlurHashes == null) - { - dto.ImageBlurHashes = new Dictionary>(); - } + dto.ImageBlurHashes ??= new Dictionary>(); dto.ImageBlurHashes[imageType] = hashes; } @@ -898,10 +892,7 @@ namespace Emby.Server.Implementations.Dto dto.Taglines = new string[] { item.Tagline }; } - if (dto.Taglines == null) - { - dto.Taglines = Array.Empty(); - } + dto.Taglines ??= Array.Empty(); } dto.Type = item.GetBaseItemKind(); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a44edad16..4d207471a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -176,10 +176,7 @@ namespace Emby.Server.Implementations.Library { lock (_rootFolderSyncLock) { - if (_rootFolder == null) - { - _rootFolder = CreateRootFolder(); - } + _rootFolder ??= CreateRootFolder(); } } diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index 9b4cd7a3d..6f29bc649 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -35,14 +35,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - var season = parent as Season; - // Just in case the user decided to nest episodes. // Not officially supported but in some cases we can handle it. - if (season == null) - { - season = parent.GetParents().OfType().FirstOrDefault(); - } + + var season = parent as Season ?? parent.GetParents().OfType().FirstOrDefault(); // If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something // Also handle flat tv folders @@ -55,11 +51,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV if (episode != null) { - var series = parent as Series; - if (series == null) - { - series = parent.GetParents().OfType().FirstOrDefault(); - } + var series = parent as Series ?? parent.GetParents().OfType().FirstOrDefault(); if (series != null) { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 665fbfa0f..28a2095e1 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2237,14 +2237,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var enabledTimersForSeries = new List(); foreach (var timer in allTimers) { - var existingTimer = _timerProvider.GetTimer(timer.Id); - - if (existingTimer == null) - { - existingTimer = string.IsNullOrWhiteSpace(timer.ProgramId) + var existingTimer = _timerProvider.GetTimer(timer.Id) + ?? (string.IsNullOrWhiteSpace(timer.ProgramId) ? null - : _timerProvider.GetTimerByProgramId(timer.ProgramId); - } + : _timerProvider.GetTimerByProgramId(timer.ProgramId)); if (existingTimer == null) { diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 1926e738f..9af65cabb 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -787,14 +787,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings { var channelNumber = GetChannelNumber(channel); - var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase)); - if (station == null) - { - station = new ScheduleDirect.Station + var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase)) + ?? new ScheduleDirect.Station { stationID = channel.stationID }; - } var channelInfo = new ChannelInfo { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 63a3146aa..1145d8aa1 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -987,10 +987,7 @@ namespace Emby.Server.Implementations.LiveTv var externalProgramId = programTuple.Item2; string externalSeriesId = programTuple.Item3; - if (timerList == null) - { - timerList = (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items; - } + timerList ??= (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items; var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase)); var foundSeriesTimer = false; @@ -1018,10 +1015,7 @@ namespace Emby.Server.Implementations.LiveTv continue; } - if (seriesTimerList == null) - { - seriesTimerList = (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items; - } + seriesTimerList ??= (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items; var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase)); @@ -1974,10 +1968,7 @@ namespace Emby.Server.Implementations.LiveTv }; } - if (service == null) - { - service = _services[0]; - } + service ??= _services[0]; var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index c32ca2fb6..4aa5832b1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -421,10 +421,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun string audioCodec = channelInfo.AudioCodec; - if (!videoBitrate.HasValue) - { - videoBitrate = isHd ? 15000000 : 2000000; - } + videoBitrate ??= isHd ? 15000000 : 2000000; int? audioBitrate = isHd ? 448000 : 192000; diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index fd2ee6b7a..14df20936 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -44,12 +44,7 @@ namespace Emby.Server.Implementations.Plugins { get { - if (_httpClientFactory == null) - { - _httpClientFactory = _appHost.Resolve(); - } - - return _httpClientFactory; + return _httpClientFactory ?? (_httpClientFactory = _appHost.Resolve()); } } @@ -276,11 +271,7 @@ namespace Emby.Server.Implementations.Plugins // If no version is given, return the current instance. var plugins = _plugins.Where(p => p.Id.Equals(id)).ToList(); - plugin = plugins.FirstOrDefault(p => p.Instance != null); - if (plugin == null) - { - plugin = plugins.OrderByDescending(p => p.Version).FirstOrDefault(); - } + plugin = plugins.FirstOrDefault(p => p.Instance != null) ?? plugins.OrderByDescending(p => p.Version).FirstOrDefault(); } else { diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 61dccaa19..101d9b537 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -301,12 +301,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { get { - if (_id == null) - { - _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture); - } - - return _id; + return _id ??= ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture); } } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 6f21ec31e..6844152ea 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1475,10 +1475,7 @@ namespace Emby.Server.Implementations.Session user = _userManager.GetUserById(request.UserId); } - if (user == null) - { - user = _userManager.GetUserByName(request.Username); - } + user ??= _userManager.GetUserByName(request.Username); if (enforcePassword) { diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index adec86a10..7a6130719 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -207,12 +207,7 @@ namespace Jellyfin.Api.Controllers var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId)); // Select the un-instanced one first. - var plugin = plugins.FirstOrDefault(p => p.Instance == null); - if (plugin == null) - { - // Then by the status. - plugin = plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault(); - } + var plugin = plugins.FirstOrDefault(p => p.Instance == null) ?? plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault(); if (plugin != null) { diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 6c22050a7..73bdf9018 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -228,10 +228,7 @@ namespace Jellyfin.Api.Controllers itemWithImage = GetParentWithImage(item, ImageType.Thumb); } - if (itemWithImage == null) - { - itemWithImage = GetParentWithImage(item, ImageType.Thumb); - } + itemWithImage ??= GetParentWithImage(item, ImageType.Thumb); if (itemWithImage != null) { diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 583e613b4..8cffe9c4c 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -292,10 +292,7 @@ namespace Jellyfin.Api.Helpers } } - if (profile == null) - { - profile = dlnaManager.GetDefaultProfile(); - } + profile ??= dlnaManager.GetDefaultProfile(); var audioCodec = state.ActualOutputAudioCodec; diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index fb3ef9b12..7156ce618 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -400,10 +400,7 @@ namespace MediaBrowser.Common.Net private bool ResolveHost() { // When was the last time we resolved? - if (_lastResolved == null) - { - _lastResolved = DateTime.UtcNow; - } + _lastResolved ??= DateTime.UtcNow; // If we haven't resolved before, or our timer has run out... if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout))) diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs index 99c226f50..e074cc6a0 100644 --- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs +++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs @@ -105,10 +105,7 @@ namespace MediaBrowser.Common.Plugins { lock (_configurationSyncLock) { - if (_configuration == null) - { - _configuration = LoadConfiguration(); - } + _configuration ??= LoadConfiguration(); } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 1b69c6646..32ae15498 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -106,15 +106,10 @@ namespace MediaBrowser.Controller.Entities { get { - if (_themeSongIds == null) - { - _themeSongIds = GetExtras() - .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong) - .Select(song => song.Id) - .ToArray(); - } - - return _themeSongIds; + return _themeSongIds ??= GetExtras() + .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong) + .Select(song => song.Id) + .ToArray(); } private set @@ -128,15 +123,10 @@ namespace MediaBrowser.Controller.Entities { get { - if (_themeVideoIds == null) - { - _themeVideoIds = GetExtras() - .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo) - .Select(song => song.Id) - .ToArray(); - } - - return _themeVideoIds; + return _themeVideoIds ??= GetExtras() + .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo) + .Select(song => song.Id) + .ToArray(); } private set diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index b1da4d64c..fec83dd94 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -75,10 +75,7 @@ namespace MediaBrowser.Controller.Entities public override List GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) { - if (query == null) - { - query = new InternalItemsQuery(user); - } + query ??= new InternalItemsQuery(user); query.EnableTotalRecordCount = false; var result = GetItemList(query); diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index f86f7df25..f9086066d 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -147,10 +147,7 @@ namespace MediaBrowser.Controller.Library throw new ArgumentException("The path was empty or null.", nameof(path)); } - if (AdditionalLocations == null) - { - AdditionalLocations = new List(); - } + AdditionalLocations ??= new List(); AdditionalLocations.Add(path); } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index c9c168c4c..3c93cfc79 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -126,10 +126,7 @@ namespace MediaBrowser.Controller.Playlists private List GetPlayableItems(User user, InternalItemsQuery query) { - if (query == null) - { - query = new InternalItemsQuery(user); - } + query ??= new InternalItemsQuery(user); query.IsFolder = false; diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index b92b83701..db0ef7072 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs @@ -45,10 +45,7 @@ namespace MediaBrowser.Controller.Providers if (copy.RefreshPaths != null && copy.RefreshPaths.Length > 0) { - if (RefreshPaths == null) - { - RefreshPaths = Array.Empty(); - } + RefreshPaths ??= Array.Empty(); RefreshPaths = copy.RefreshPaths.ToArray(); } diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index 864cb3050..98c7eadfe 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -37,10 +37,7 @@ namespace MediaBrowser.Controller.Providers public void AddPerson(PersonInfo p) { - if (People == null) - { - People = new List(); - } + People ??= new List(); PeopleHelper.AddPerson(People, p); } @@ -54,16 +51,15 @@ namespace MediaBrowser.Controller.Providers { People = new List(); } - - People.Clear(); + else + { + People.Clear(); + } } public UserItemData GetOrAddUserData(string userId) { - if (UserDataList == null) - { - UserDataList = new List(); - } + UserDataList ??= new List(); UserItemData userData = null; diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index ee2e5fcde..2e96f8cb0 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1187,43 +1187,28 @@ namespace MediaBrowser.MediaEncoding.Probing FetchStudios(audio, tags, "label"); // These support mulitple values, but for now we only store the first. - var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID")); - } + var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID")); audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID")); audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID")); audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID")); audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID")); audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb); } @@ -1290,15 +1275,7 @@ namespace MediaBrowser.MediaEncoding.Probing private IEnumerable GetSplitWhitelist() { - if (_splitWhiteList == null) - { - _splitWhiteList = new List - { - "AC/DC" - }; - } - - return _splitWhiteList; + return _splitWhiteList ??= new List { "AC/DC" }; } /// diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 09d14dc6a..ce4b0ec92 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -123,10 +123,7 @@ namespace MediaBrowser.Model.Entities else { // Ensure it exists - if (instance.ProviderIds == null) - { - instance.ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + instance.ProviderIds ??= new Dictionary(StringComparer.OrdinalIgnoreCase); instance.ProviderIds[name] = value; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 74849a522..f049cc81f 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -111,10 +111,7 @@ namespace MediaBrowser.Providers.MediaInfo } } - if (streamFileNames == null) - { - streamFileNames = Array.Empty(); - } + streamFileNames ??= Array.Empty(); mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 1f3d9acff..8d62343cb 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -256,10 +256,7 @@ namespace MediaBrowser.Providers.Subtitles } catch (Exception ex) { - if (exceptionToThrow == null) - { - exceptionToThrow = ex; - } + exceptionToThrow ??= ex; } finally { diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 8f1f0fa61..f448ad38b 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -415,10 +415,7 @@ namespace Rssdp.Infrastructure { lock (_SendSocketSynchroniser) { - if (_sendSockets == null) - { - _sendSockets = CreateSocketAndListenForResponsesAsync(); - } + _sendSockets ??= CreateSocketAndListenForResponsesAsync(); } } } From e432796f6f0f500830b1c90c233c4e4c07287190 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 14:39:50 +0200 Subject: [PATCH 391/402] Minor improvements --- .../Probing/ProbeResultNormalizer.cs | 11 ++++------- MediaBrowser.Providers/Subtitles/SubtitleManager.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 2e96f8cb0..884ec0a29 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.MediaEncoding.Probing private readonly ILogger _logger; private readonly ILocalizationManager _localization; - private List _splitWhiteList = null; + private string[] _splitWhiteList; public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization) { @@ -37,6 +37,8 @@ namespace MediaBrowser.MediaEncoding.Probing _localization = localization; } + private IReadOnlyList SplitWhitelist => _splitWhiteList ??= new string[] { "AC/DC" }; + public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType? videoType, bool isAudio, string path, MediaProtocol protocol) { var info = new MediaInfo @@ -1254,7 +1256,7 @@ namespace MediaBrowser.MediaEncoding.Probing var artistsFound = new List(); - foreach (var whitelistArtist in GetSplitWhitelist()) + foreach (var whitelistArtist in SplitWhitelist) { var originalVal = val; val = val.Replace(whitelistArtist, "|", StringComparison.OrdinalIgnoreCase); @@ -1273,11 +1275,6 @@ namespace MediaBrowser.MediaEncoding.Probing return artistsFound; } - private IEnumerable GetSplitWhitelist() - { - return _splitWhiteList ??= new List { "AC/DC" }; - } - /// /// Gets the studios from the tags collection. /// diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 8d62343cb..bf0c853ae 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -207,7 +207,7 @@ namespace MediaBrowser.Providers.Subtitles { var mediaFolderPath = Path.GetFullPath(Path.Combine(video.ContainingFolderPath, saveFileName)); // TODO: Add some error handling to the API user: return BadRequest("Could not save subtitle, bad path."); - if (mediaFolderPath.StartsWith(video.ContainingFolderPath)) + if (mediaFolderPath.StartsWith(video.ContainingFolderPath, StringComparison.Ordinal)) { savePaths.Add(mediaFolderPath); } @@ -216,7 +216,7 @@ namespace MediaBrowser.Providers.Subtitles var internalPath = Path.GetFullPath(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); // TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path."); - if (internalPath.StartsWith(video.GetInternalMetadataPath())) + if (internalPath.StartsWith(video.GetInternalMetadataPath(), StringComparison.Ordinal)) { savePaths.Add(internalPath); } @@ -234,7 +234,7 @@ namespace MediaBrowser.Providers.Subtitles private async Task TrySaveToFiles(Stream stream, List savePaths) { - Exception exceptionToThrow = null; + List exs = null; foreach (var savePath in savePaths) { @@ -256,7 +256,7 @@ namespace MediaBrowser.Providers.Subtitles } catch (Exception ex) { - exceptionToThrow ??= ex; + (exs ??= new List()).Add(ex); } finally { @@ -266,9 +266,9 @@ namespace MediaBrowser.Providers.Subtitles stream.Position = 0; } - if (exceptionToThrow != null) + if (exs != null) { - throw exceptionToThrow; + throw new AggregateException(exs); } } From 4479713e047f0e51450ed343472d8c43ab57e00f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 14:44:53 +0200 Subject: [PATCH 392/402] MediaStream: Replace string.IndexOf with string.Contains where possible --- MediaBrowser.Model/Entities/MediaStream.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index ade9d7e8d..e644c9ba7 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -163,7 +163,7 @@ namespace MediaBrowser.Model.Entities foreach (var tag in attributes) { // Keep Tags that are not already in Title. - if (Title.IndexOf(tag, StringComparison.OrdinalIgnoreCase) == -1) + if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) { result.Append(" - ").Append(tag); } @@ -202,7 +202,7 @@ namespace MediaBrowser.Model.Entities foreach (var tag in attributes) { // Keep Tags that are not already in Title. - if (Title.IndexOf(tag, StringComparison.OrdinalIgnoreCase) == -1) + if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase)) { result.Append(" - ").Append(tag); } @@ -522,9 +522,9 @@ namespace MediaBrowser.Model.Entities // sub = external .sub file - return codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) == -1 && - codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) == -1 && - codec.IndexOf("dvbsub", StringComparison.OrdinalIgnoreCase) == -1 && + return !codec.Contains("pgs", StringComparison.OrdinalIgnoreCase) && + !codec.Contains("dvd", StringComparison.OrdinalIgnoreCase) && + !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase) && !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase) && !string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase); } From 787bcd4a83ea212e6ba4f93dcc4ce6788b950410 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 14:45:08 +0200 Subject: [PATCH 393/402] Remove dead code --- .../Entities/PeopleHelper.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs index 1f3758a73..687ce1ec8 100644 --- a/MediaBrowser.Controller/Entities/PeopleHelper.cs +++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs @@ -100,23 +100,5 @@ namespace MediaBrowser.Controller.Entities existing.SetProviderId(id.Key, id.Value); } } - - public static bool ContainsPerson(List people, string name) - { - if (string.IsNullOrEmpty(name)) - { - throw new ArgumentNullException(nameof(name)); - } - - foreach (var i in people) - { - if (string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } } } From bcb4010db615b7c732856629553cba4e7ccc3358 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 15:30:32 +0200 Subject: [PATCH 394/402] More improvements --- .../Library/Resolvers/ItemResolver.cs | 12 ++++++------ .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../Library/Resolvers/PhotoAlbumResolver.cs | 12 ++++++------ .../Library/Resolvers/PhotoResolver.cs | 2 +- .../Library/Resolvers/TV/SeasonResolver.cs | 2 +- .../Library/Resolvers/TV/SeriesResolver.cs | 18 ++---------------- .../Entities/CollectionFolder.cs | 1 - .../Library/ItemResolveArgs.cs | 18 +++++++++--------- .../Resolvers/BaseItemResolver.cs | 12 ++++++------ 9 files changed, 32 insertions(+), 47 deletions(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs index 9ca76095b..92fb2a753 100644 --- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs @@ -11,6 +11,12 @@ namespace Emby.Server.Implementations.Library.Resolvers public abstract class ItemResolver : IItemResolver where T : BaseItem, new() { + /// + /// Gets the priority. + /// + /// The priority. + public virtual ResolverPriority Priority => ResolverPriority.First; + /// /// Resolves the specified args. /// @@ -21,12 +27,6 @@ namespace Emby.Server.Implementations.Library.Resolvers return null; } - /// - /// Gets the priority. - /// - /// The priority. - public virtual ResolverPriority Priority => ResolverPriority.First; - /// /// Sets initial values on the newly resolved item. /// diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 714bc3a84..16bf4dc4a 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -376,7 +376,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies { var multiDiscFolders = new List(); - var libraryOptions = args.GetLibraryOptions(); + var libraryOptions = args.LibraryOptions; var supportPhotos = string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && libraryOptions.EnablePhotos; var photos = new List(); diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs index 3ac837057..204c8a62e 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs @@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.Library.Resolvers public class PhotoAlbumResolver : FolderResolver { private readonly IImageProcessor _imageProcessor; - private ILibraryManager _libraryManager; + private readonly ILibraryManager _libraryManager; /// /// Initializes a new instance of the class. @@ -26,6 +26,9 @@ namespace Emby.Server.Implementations.Library.Resolvers _libraryManager = libraryManager; } + /// + public override ResolverPriority Priority => ResolverPriority.Second; + /// /// Resolves the specified args. /// @@ -39,8 +42,8 @@ namespace Emby.Server.Implementations.Library.Resolvers // Must be an image file within a photo collection var collectionType = args.GetCollectionType(); - if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) || - (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos)) + if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) + || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.LibraryOptions.EnablePhotos)) { if (HasPhotos(args)) { @@ -84,8 +87,5 @@ namespace Emby.Server.Implementations.Library.Resolvers return false; } - - /// - public override ResolverPriority Priority => ResolverPriority.Second; } } diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index bcfcee9c6..3cb6542cf 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library.Resolvers var collectionType = args.CollectionType; if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) - || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos)) + || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.LibraryOptions.EnablePhotos)) { if (IsImageFile(args.Path, _imageProcessor)) { diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index 3332e1806..768e2e4f5 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -88,7 +88,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV CultureInfo.InvariantCulture, _localization.GetLocalizedString("NameSeasonNumber"), seasonNumber, - args.GetLibraryOptions().PreferredMetadataLanguage); + args.LibraryOptions.PreferredMetadataLanguage); } return season; diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 4a9d2cf8c..8fc3e3e75 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -19,19 +19,16 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// public class SeriesResolver : FolderResolver { - private readonly IFileSystem _fileSystem; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; /// /// Initializes a new instance of the class. /// - /// The file system. /// The logger. /// The library manager. - public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager) + public SeriesResolver(ILogger logger, ILibraryManager libraryManager) { - _fileSystem = fileSystem; _logger = logger; _libraryManager = libraryManager; } @@ -59,15 +56,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV var collectionType = args.GetCollectionType(); if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { - // if (args.ContainsFileSystemEntryByName("tvshow.nfo")) - //{ - // return new Series - // { - // Path = args.Path, - // Name = Path.GetFileName(args.Path) - // }; - //} - var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path); if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { @@ -100,7 +88,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, false)) + if (IsSeriesFolder(args.Path, args.FileSystemChildren, _logger, _libraryManager, false)) { return new Series { @@ -117,8 +105,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV public static bool IsSeriesFolder( string path, IEnumerable fileSystemChildren, - IDirectoryService directoryService, - IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, bool isTvContentType) diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 347d5b73c..bc5f38256 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -61,7 +61,6 @@ namespace MediaBrowser.Controller.Entities try { var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions; - if (result == null) { return new LibraryOptions(); diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index f9086066d..5f9aed341 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -14,14 +14,14 @@ namespace MediaBrowser.Controller.Library /// These are arguments relating to the file system that are collected once and then referred to /// whenever needed. Primarily for entity resolution. /// - public class ItemResolveArgs : EventArgs + public class ItemResolveArgs { /// /// The _app paths. /// private readonly IServerApplicationPaths _appPaths; - public IDirectoryService DirectoryService { get; private set; } + private LibraryOptions _libraryOptions; /// /// Initializes a new instance of the class. @@ -34,17 +34,18 @@ namespace MediaBrowser.Controller.Library DirectoryService = directoryService; } + public IDirectoryService DirectoryService { get; } + /// /// Gets the file system children. /// /// The file system children. public FileSystemMetadata[] FileSystemChildren { get; set; } - public LibraryOptions LibraryOptions { get; set; } - - public LibraryOptions GetLibraryOptions() + public LibraryOptions LibraryOptions { - return LibraryOptions ?? (LibraryOptions = Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent)); + get => _libraryOptions ??= Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent); + set => _libraryOptions = value; } /// @@ -139,7 +140,7 @@ namespace MediaBrowser.Controller.Library /// Adds the additional location. /// /// The path. - /// + /// is null or empty. public void AddAdditionalLocation(string path) { if (string.IsNullOrEmpty(path)) @@ -148,7 +149,6 @@ namespace MediaBrowser.Controller.Library } AdditionalLocations ??= new List(); - AdditionalLocations.Add(path); } @@ -172,7 +172,7 @@ namespace MediaBrowser.Controller.Library /// /// The name. /// FileSystemInfo. - /// + /// is null or empty. public FileSystemMetadata GetFileSystemEntryByName(string name) { if (string.IsNullOrEmpty(name)) diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs index 25128a5cd..a904c7424 100644 --- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs @@ -10,6 +10,12 @@ namespace MediaBrowser.Controller.Resolvers public abstract class ItemResolver : IItemResolver where T : BaseItem, new() { + /// + /// Gets the priority. + /// + /// The priority. + public virtual ResolverPriority Priority => ResolverPriority.First; + /// /// Resolves the specified args. /// @@ -20,12 +26,6 @@ namespace MediaBrowser.Controller.Resolvers return null; } - /// - /// Gets the priority. - /// - /// The priority. - public virtual ResolverPriority Priority => ResolverPriority.First; - /// /// Sets initial values on the newly resolved item. /// From 47e7c1356c1364e5e834ce37cd4a9ace5e2d838e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 16:43:20 +0200 Subject: [PATCH 395/402] PathExtensions: Fix index out of bounds in TryReplaceSubPath Fixes #5977 --- Emby.Server.Implementations/Library/PathExtensions.cs | 10 ++++++++-- .../Library/PathExtensionsTests.cs | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 770cf6bb0..0de4edb7e 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -96,8 +96,14 @@ namespace Emby.Server.Implementations.Library // We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results // when the sub path matches a similar but in-complete subpath var oldSubPathEndsWithSeparator = subPath[^1] == newDirectorySeparatorChar; - if (!path.StartsWith(subPath, StringComparison.OrdinalIgnoreCase) - || (!oldSubPathEndsWithSeparator && path[subPath.Length] != newDirectorySeparatorChar)) + if (!path.StartsWith(subPath, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (path.Length > subPath.Length + && !oldSubPathEndsWithSeparator + && path[subPath.Length] != newDirectorySeparatorChar) { return false; } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index e5508243f..c5cc056f5 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -33,6 +33,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff/", "/home/jeff/myfile.mkv")] [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/home/jeff/", "/home/jeff/myfile.mkv")] [InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/", "/myfile.mkv")] + [InlineData("/o", "/o", "/s", "/s")] // regression test for #5977 public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, string? expectedResult) { Assert.True(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result)); From ddb04dc12b8bdf33c2020cb1c539664463e61bc3 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 8 Jan 2021 23:57:27 +0100 Subject: [PATCH 396/402] Use new ReadAllLines extensions --- .../LiveTv/EmbyTV/EncodedRecorder.cs | 7 ++-- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 14 ++++---- .../LiveTv/TunerHosts/M3uParser.cs | 34 +++++++------------ .../Localization/LocalizationManager.cs | 10 +++--- Jellyfin.Api/Helpers/HlsHelpers.cs | 5 +-- .../Extensions/StreamExtensions.cs | 20 +++++++++-- .../Studios/StudiosImageProvider.cs | 9 +++-- 7 files changed, 48 insertions(+), 51 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 44a8cdee4..9372b0f6c 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -10,6 +10,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Json; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -307,13 +308,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { using (var reader = new StreamReader(source)) { - while (!reader.EndOfStream) + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) { - var line = await reader.ReadLineAsync().ConfigureAwait(false); - var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line); - await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + await target.WriteAsync(bytes.AsMemory()).ConfigureAwait(false); await target.FlushAsync().ConfigureAwait(false); } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 4aa5832b1..324109bcf 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -182,16 +182,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); using var sr = new StreamReader(stream, System.Text.Encoding.UTF8); var tuners = new List(); - while (!sr.EndOfStream) + await foreach (var line in sr.ReadAllLinesAsync().ConfigureAwait(false)) { - string line = StripXML(sr.ReadLine()); - if (line.Contains("Channel", StringComparison.Ordinal)) + string stripedLine = StripXML(line); + if (stripedLine.Contains("Channel", StringComparison.Ordinal)) { LiveTvTunerStatus status; - var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); - var name = line.Substring(0, index - 1); - var currentChannel = line.Substring(index + 7); - if (currentChannel != "none") + var index = stripedLine.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); + var name = stripedLine.Substring(0, index - 1); + var currentChannel = stripedLine.Substring(index + 7); + if (string.Equals(currentChannel, "none", StringComparison.Ordinal)) { status = LiveTvTunerStatus.LiveTv; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index cc30a516d..84d416149 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -35,16 +35,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts // Read the file and display it line by line. using (var reader = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false))) { - return GetChannels(reader, channelIdPrefix, info.Id); - } - } - - public List ParseString(string text, string channelIdPrefix, string tunerHostId) - { - // Read the file and display it line by line. - using (var reader = new StringReader(text)) - { - return GetChannels(reader, channelIdPrefix, tunerHostId); + return await GetChannelsAsync(reader, channelIdPrefix, info.Id).ConfigureAwait(false); } } @@ -70,43 +61,42 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private const string ExtInfPrefix = "#EXTINF:"; - private List GetChannels(TextReader reader, string channelIdPrefix, string tunerHostId) + private async Task> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId) { var channels = new List(); - string line; string extInf = string.Empty; - while ((line = reader.ReadLine()) != null) + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) { - line = line.Trim(); - if (string.IsNullOrWhiteSpace(line)) + var trimmedLine = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmedLine)) { continue; } - if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase)) + if (trimmedLine.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase)) { continue; } - if (line.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase)) + if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase)) { - extInf = line.Substring(ExtInfPrefix.Length).Trim(); + extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim(); _logger.LogInformation("Found m3u channel: {0}", extInf); } - else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith('#')) + else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#')) { - var channel = GetChannelnfo(extInf, tunerHostId, line); + var channel = GetChannelnfo(extInf, tunerHostId, trimmedLine); if (string.IsNullOrWhiteSpace(channel.Id)) { - channel.Id = channelIdPrefix + line.GetMD5().ToString("N", CultureInfo.InvariantCulture); + channel.Id = channelIdPrefix + trimmedLine.GetMD5().ToString("N", CultureInfo.InvariantCulture); } else { channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N", CultureInfo.InvariantCulture); } - channel.Path = line; + channel.Path = trimmedLine; channels.Add(channel); extInf = string.Empty; } diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 46858b4fb..220e423bf 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Json; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Entities; @@ -72,8 +73,7 @@ namespace Emby.Server.Implementations.Localization using (var str = _assembly.GetManifestResourceStream(resource)) using (var reader = new StreamReader(str)) { - string line; - while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) { if (string.IsNullOrWhiteSpace(line)) { @@ -118,10 +118,8 @@ namespace Emby.Server.Implementations.Localization using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) using (var reader = new StreamReader(stream)) { - while (!reader.EndOfStream) + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) { - var line = await reader.ReadLineAsync().ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(line)) { continue; @@ -179,7 +177,7 @@ namespace Emby.Server.Implementations.Localization /// public IEnumerable GetCountries() { - StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); + using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions); } diff --git a/Jellyfin.Api/Helpers/HlsHelpers.cs b/Jellyfin.Api/Helpers/HlsHelpers.cs index 18e23fb5c..d0666034e 100644 --- a/Jellyfin.Api/Helpers/HlsHelpers.cs +++ b/Jellyfin.Api/Helpers/HlsHelpers.cs @@ -118,10 +118,7 @@ namespace Jellyfin.Api.Helpers /// The playlist text as a string. public static string GetLivePlaylistText(string path, StreamState state) { - using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using var reader = new StreamReader(stream); - - var text = reader.ReadToEnd(); + var text = File.ReadAllText(path); var segmentFormat = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.'); if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Common/Extensions/StreamExtensions.cs b/MediaBrowser.Common/Extensions/StreamExtensions.cs index cd77be7b2..d49bf1d46 100644 --- a/MediaBrowser.Common/Extensions/StreamExtensions.cs +++ b/MediaBrowser.Common/Extensions/StreamExtensions.cs @@ -35,11 +35,11 @@ namespace MediaBrowser.Common.Extensions } /// - /// Reads all lines in the . + /// Reads all lines in the . /// - /// The to read from. + /// The to read from. /// All lines in the stream. - public static IEnumerable ReadAllLines(this StreamReader reader) + public static IEnumerable ReadAllLines(this TextReader reader) { string? line; while ((line = reader.ReadLine()) != null) @@ -47,5 +47,19 @@ namespace MediaBrowser.Common.Extensions yield return line; } } + + /// + /// Reads all lines in the . + /// + /// The to read from. + /// All lines in the stream. + public static async IAsyncEnumerable ReadAllLinesAsync(this TextReader reader) + { + string? line; + while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) + { + yield return line; + } + } } } diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index 5fcf6d9aa..5ec9a02cb 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -177,13 +178,11 @@ namespace MediaBrowser.Providers.Studios { var lines = new List(); - while (!reader.EndOfStream) + foreach (var line in reader.ReadAllLines()) { - var text = reader.ReadLine(); - - if (!string.IsNullOrWhiteSpace(text)) + if (!string.IsNullOrWhiteSpace(line)) { - lines.Add(text); + lines.Add(line); } } From ad1d9d9a23c4bff7a485a61fe41639a7d1b3491e Mon Sep 17 00:00:00 2001 From: wehrstedt Date: Thu, 6 May 2021 23:07:32 +0200 Subject: [PATCH 397/402] fixed no channel icons when using tvheadend (#5996) Co-authored-by: Cody Robibero Co-authored-by: Maximilian Wehrstedt --- MediaBrowser.Providers/Manager/ProviderManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 08c92e15a..dd497845d 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -167,7 +167,7 @@ namespace MediaBrowser.Providers.Manager throw new HttpRequestException("Invalid image received.", null, response.StatusCode); } - var contentType = response.Content.Headers.ContentType.MediaType; + var contentType = response.Content.Headers.ContentType?.MediaType; // Workaround for tvheadend channel icons // TODO: Isolate this hack into the tvh plugin From 4b9a64c18cff58938dab0c0770147a5e48c833f9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 6 May 2021 23:16:52 +0200 Subject: [PATCH 398/402] Abstract JsonDelimitedArrayConverter --- .../JsonCommaDelimitedArrayConverter.cs | 63 +-------------- .../Converters/JsonDelimitedArrayConverter.cs | 81 +++++++++++++++++++ .../JsonPipeDelimitedArrayConverter.cs | 64 +-------------- 3 files changed, 87 insertions(+), 121 deletions(-) create mode 100644 MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs index 55c4665e8..127a41a06 100644 --- a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs @@ -9,73 +9,16 @@ namespace MediaBrowser.Common.Json.Converters /// Convert comma delimited string to array of type. /// /// Type to convert to. - public class JsonCommaDelimitedArrayConverter : JsonConverter + public sealed class JsonCommaDelimitedArrayConverter : JsonDelimitedArrayConverter { - private readonly TypeConverter _typeConverter; - /// /// Initializes a new instance of the class. /// - public JsonCommaDelimitedArrayConverter() + public JsonCommaDelimitedArrayConverter() : base() { - _typeConverter = TypeDescriptor.GetConverter(typeof(T)); } /// - public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - { - return null; - } - - if (reader.TokenType == JsonTokenType.String) - { - // GetString can't return null here because we already handled it above - var stringEntries = reader.GetString()!.Split(',', StringSplitOptions.RemoveEmptyEntries); - if (stringEntries.Length == 0) - { - return Array.Empty(); - } - - var parsedValues = new object[stringEntries.Length]; - var convertedCount = 0; - for (var i = 0; i < stringEntries.Length; i++) - { - try - { - parsedValues[i] = _typeConverter.ConvertFrom(stringEntries[i].Trim()); - convertedCount++; - } - catch (FormatException) - { - // TODO log when upgraded to .Net6 - // https://github.com/dotnet/runtime/issues/42975 - // _logger.LogDebug(e, "Error converting value."); - } - } - - var typedValues = new T[convertedCount]; - var typedValueIndex = 0; - for (var i = 0; i < stringEntries.Length; i++) - { - if (parsedValues[i] != null) - { - typedValues.SetValue(parsedValues[i], typedValueIndex); - typedValueIndex++; - } - } - - return typedValues; - } - - return JsonSerializer.Deserialize(ref reader, options); - } - - /// - public override void Write(Utf8JsonWriter writer, T[]? value, JsonSerializerOptions options) - { - throw new NotImplementedException(); - } + protected override char Delimiter => ','; } } diff --git a/MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs new file mode 100644 index 000000000..b691798c9 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs @@ -0,0 +1,81 @@ +using System; +using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Convert delimited string to array of type. + /// + /// Type to convert to. + public abstract class JsonDelimitedArrayConverter : JsonConverter + { + private readonly TypeConverter _typeConverter; + + /// + /// Initializes a new instance of the class. + /// + protected JsonDelimitedArrayConverter() + { + _typeConverter = TypeDescriptor.GetConverter(typeof(T)); + } + + /// + /// Gets the array delimiter. + /// + protected virtual char Delimiter { get; } + + /// + public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + // GetString can't return null here because we already handled it above + var stringEntries = reader.GetString()?.Split(Delimiter, StringSplitOptions.RemoveEmptyEntries); + if (stringEntries == null || stringEntries.Length == 0) + { + return Array.Empty(); + } + + var parsedValues = new object[stringEntries.Length]; + var convertedCount = 0; + for (var i = 0; i < stringEntries.Length; i++) + { + try + { + parsedValues[i] = _typeConverter.ConvertFrom(stringEntries[i].Trim()); + convertedCount++; + } + catch (FormatException) + { + // TODO log when upgraded to .Net6 + // https://github.com/dotnet/runtime/issues/42975 + // _logger.LogDebug(e, "Error converting value."); + } + } + + var typedValues = new T[convertedCount]; + var typedValueIndex = 0; + for (var i = 0; i < stringEntries.Length; i++) + { + if (parsedValues[i] != null) + { + typedValues.SetValue(parsedValues[i], typedValueIndex); + typedValueIndex++; + } + } + + return typedValues; + } + + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, T[]? value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + } +} diff --git a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs index c83657b5f..a8f6cfbec 100644 --- a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs @@ -9,74 +9,16 @@ namespace MediaBrowser.Common.Json.Converters /// Convert Pipe delimited string to array of type. /// /// Type to convert to. - public class JsonPipeDelimitedArrayConverter : JsonConverter + public sealed class JsonPipeDelimitedArrayConverter : JsonDelimitedArrayConverter { - private readonly TypeConverter _typeConverter; - /// /// Initializes a new instance of the class. /// - public JsonPipeDelimitedArrayConverter() + public JsonPipeDelimitedArrayConverter() : base() { - _typeConverter = TypeDescriptor.GetConverter(typeof(T)); } /// - public override T[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - { - return Array.Empty(); - } - - if (reader.TokenType == JsonTokenType.String) - { - // GetString can't return null here because we already handled it above - var stringEntries = reader.GetString()!.Split('|', StringSplitOptions.RemoveEmptyEntries); - if (stringEntries.Length == 0) - { - return Array.Empty(); - } - - var parsedValues = new object[stringEntries.Length]; - var convertedCount = 0; - for (var i = 0; i < stringEntries.Length; i++) - { - try - { - parsedValues[i] = _typeConverter.ConvertFrom(stringEntries[i].Trim()); - convertedCount++; - } - catch (FormatException) - { - // TODO log when upgraded to .Net6 - // https://github.com/dotnet/runtime/issues/42975 - // _logger.LogDebug(e, "Error converting value."); - } - } - - var typedValues = new T[convertedCount]; - var typedValueIndex = 0; - for (var i = 0; i < stringEntries.Length; i++) - { - if (parsedValues[i] != null) - { - typedValues.SetValue(parsedValues[i], typedValueIndex); - typedValueIndex++; - } - } - - return typedValues; - } - - // can't return null here because we already handled it above - return JsonSerializer.Deserialize(ref reader, options)!; - } - - /// - public override void Write(Utf8JsonWriter writer, T[] value, JsonSerializerOptions options) - { - throw new NotImplementedException(); - } + protected override char Delimiter => '|'; } } From fb090df0b59b71d7f143d2181d46f18943bbc35e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 May 2021 00:39:20 +0200 Subject: [PATCH 399/402] Enable nullable reference types for MediaBrowser.Controller --- Jellyfin.Api/Controllers/LibraryController.cs | 2 +- .../Authentication/AuthenticationResult.cs | 2 ++ .../Authentication/IAuthenticationProvider.cs | 2 ++ .../Authentication/IPasswordResetProvider.cs | 2 ++ .../BaseItemManager/BaseItemManager.cs | 2 ++ .../BaseItemManager/IBaseItemManager.cs | 2 ++ MediaBrowser.Controller/Channels/Channel.cs | 2 ++ .../Channels/ChannelItemInfo.cs | 2 ++ .../Channels/ChannelItemResult.cs | 2 ++ .../Channels/ChannelSearchInfo.cs | 2 ++ MediaBrowser.Controller/Channels/IChannel.cs | 2 ++ .../Channels/IChannelManager.cs | 2 ++ .../Channels/IHasCacheKey.cs | 2 ++ .../Channels/ISearchableChannel.cs | 2 ++ .../Channels/InternalChannelFeatures.cs | 2 ++ .../Channels/InternalChannelItemQuery.cs | 2 ++ .../Collections/CollectionCreationOptions.cs | 2 ++ .../Collections/CollectionEvents.cs | 2 ++ .../Collections/ICollectionManager.cs | 2 ++ .../IServerConfigurationManager.cs | 2 ++ .../Devices/IDeviceManager.cs | 2 ++ MediaBrowser.Controller/Dlna/IDlnaManager.cs | 2 ++ .../Drawing/IImageEncoder.cs | 2 ++ .../Drawing/IImageProcessor.cs | 2 ++ .../Drawing/ImageCollageOptions.cs | 2 ++ .../Drawing/ImageHelper.cs | 2 ++ .../Drawing/ImageProcessingOptions.cs | 2 ++ .../Drawing/ImageProcessorExtensions.cs | 2 ++ .../Drawing/ImageStream.cs | 2 +- MediaBrowser.Controller/Dto/DtoOptions.cs | 2 ++ MediaBrowser.Controller/Dto/IDtoService.cs | 2 ++ .../Entities/AggregateFolder.cs | 2 ++ .../Entities/Audio/Audio.cs | 2 ++ .../Entities/Audio/IHasAlbumArtist.cs | 2 ++ .../Entities/Audio/IHasMusicGenres.cs | 2 ++ .../Entities/Audio/MusicAlbum.cs | 2 ++ .../Entities/Audio/MusicArtist.cs | 2 ++ .../Entities/Audio/MusicGenre.cs | 2 ++ MediaBrowser.Controller/Entities/AudioBook.cs | 2 ++ MediaBrowser.Controller/Entities/BaseItem.cs | 2 ++ .../Entities/BaseItemExtensions.cs | 2 ++ .../Entities/BasePluginFolder.cs | 2 ++ MediaBrowser.Controller/Entities/Book.cs | 2 ++ .../Entities/CollectionFolder.cs | 2 ++ .../Entities/Extensions.cs | 2 ++ MediaBrowser.Controller/Entities/Folder.cs | 2 ++ MediaBrowser.Controller/Entities/Genre.cs | 2 ++ .../Entities/ICollectionFolder.cs | 2 ++ .../Entities/IHasAspectRatio.cs | 2 ++ .../Entities/IHasDisplayOrder.cs | 2 ++ .../Entities/IHasMediaSources.cs | 2 ++ .../Entities/IHasProgramAttributes.cs | 2 ++ .../Entities/IHasSeries.cs | 2 ++ .../Entities/IHasSpecialFeatures.cs | 2 ++ .../Entities/IHasTrailers.cs | 2 ++ .../Entities/InternalItemsQuery.cs | 2 ++ .../Entities/InternalPeopleQuery.cs | 2 ++ .../Entities/ItemImageInfo.cs | 2 ++ .../Entities/LinkedChild.cs | 2 ++ .../Entities/Movies/BoxSet.cs | 2 ++ .../Entities/Movies/Movie.cs | 2 ++ .../Entities/MusicVideo.cs | 2 ++ MediaBrowser.Controller/Entities/Person.cs | 2 ++ .../Entities/PersonInfo.cs | 2 ++ MediaBrowser.Controller/Entities/Photo.cs | 2 ++ MediaBrowser.Controller/Entities/Share.cs | 2 ++ MediaBrowser.Controller/Entities/Studio.cs | 2 ++ .../Entities/TV/Episode.cs | 2 ++ MediaBrowser.Controller/Entities/TV/Season.cs | 2 ++ MediaBrowser.Controller/Entities/TV/Series.cs | 2 ++ MediaBrowser.Controller/Entities/Trailer.cs | 2 ++ .../Entities/UserItemData.cs | 2 ++ .../Entities/UserRootFolder.cs | 2 ++ MediaBrowser.Controller/Entities/UserView.cs | 2 ++ .../Entities/UserViewBuilder.cs | 2 ++ MediaBrowser.Controller/Entities/Video.cs | 2 ++ MediaBrowser.Controller/Entities/Year.cs | 2 ++ .../Events/IEventConsumer.cs | 2 +- .../Events/IEventManager.cs | 2 +- .../Events/Session/SessionEndedEventArgs.cs | 2 +- .../Events/Session/SessionStartedEventArgs.cs | 2 +- .../PluginInstallationCancelledEventArgs.cs | 2 +- .../Updates/PluginInstalledEventArgs.cs | 2 +- .../Updates/PluginInstallingEventArgs.cs | 2 +- .../Events/Updates/PluginUpdatedEventArgs.cs | 2 +- .../Extensions/StringExtensions.cs | 1 - .../IDisplayPreferencesManager.cs | 2 ++ .../IServerApplicationHost.cs | 2 ++ .../IServerApplicationPaths.cs | 2 ++ .../Library/IIntroProvider.cs | 2 ++ .../Library/ILibraryManager.cs | 2 ++ .../Library/ILiveStream.cs | 2 ++ .../Library/IMediaSourceManager.cs | 2 ++ .../Library/IMetadataSaver.cs | 2 ++ .../Library/IMusicManager.cs | 2 ++ .../Library/IUserDataManager.cs | 2 ++ .../Library/IUserManager.cs | 2 ++ .../Library/IUserViewManager.cs | 2 ++ MediaBrowser.Controller/Library/IntroInfo.cs | 2 ++ .../Library/ItemChangeEventArgs.cs | 2 ++ .../Library/ItemResolveArgs.cs | 2 ++ .../Library/LibraryManagerExtensions.cs | 2 ++ .../MetadataConfigurationExtensions.cs | 2 ++ .../Library/NameExtensions.cs | 1 - .../Library/PlaybackProgressEventArgs.cs | 2 ++ .../Library/PlaybackStartEventArgs.cs | 2 +- MediaBrowser.Controller/Library/Profiler.cs | 2 ++ .../Library/SearchHintInfo.cs | 2 ++ MediaBrowser.Controller/Library/TVUtils.cs | 4 +++- .../Library/UserDataSaveEventArgs.cs | 2 ++ MediaBrowser.Controller/LiveTv/ChannelInfo.cs | 2 ++ .../LiveTv/IListingsProvider.cs | 2 ++ .../LiveTv/ILiveTvManager.cs | 2 ++ .../LiveTv/ILiveTvService.cs | 2 ++ MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 ++ .../LiveTv/LiveTvChannel.cs | 2 ++ .../LiveTv/LiveTvProgram.cs | 2 ++ .../LiveTv/LiveTvServiceStatusInfo.cs | 2 ++ .../LiveTv/LiveTvTunerInfo.cs | 2 ++ MediaBrowser.Controller/LiveTv/ProgramInfo.cs | 2 ++ .../LiveTv/RecordingInfo.cs | 2 ++ .../LiveTv/RecordingStatusChangedEventArgs.cs | 2 ++ .../LiveTv/SeriesTimerInfo.cs | 2 ++ .../LiveTv/TimerEventInfo.cs | 2 ++ MediaBrowser.Controller/LiveTv/TimerInfo.cs | 2 ++ .../LiveTv/TunerChannelMapping.cs | 2 ++ .../MediaBrowser.Controller.csproj | 1 + .../MediaEncoding/EncodingHelper.cs | 2 ++ .../MediaEncoding/EncodingJobInfo.cs | 2 ++ .../MediaEncoding/EncodingJobOptions.cs | 2 ++ .../MediaEncoding/IAttachmentExtractor.cs | 2 ++ .../MediaEncoding/IEncodingManager.cs | 2 ++ .../MediaEncoding/IMediaEncoder.cs | 2 ++ .../MediaEncoding/ISubtitleEncoder.cs | 2 ++ .../MediaEncoding/ImageEncodingOptions.cs | 2 ++ .../MediaEncoding/JobLogger.cs | 2 ++ .../MediaEncoding/MediaEncoderHelpers.cs | 1 - .../MediaEncoding/MediaInfoRequest.cs | 2 ++ .../Net/AuthorizationInfo.cs | 2 ++ .../Net/BasePeriodicWebSocketListener.cs | 2 ++ MediaBrowser.Controller/Net/IAuthService.cs | 2 -- .../Net/IWebSocketConnection.cs | 2 ++ .../Net/SecurityException.cs | 2 -- .../Net/WebSocketMessageInfo.cs | 2 ++ .../Notifications/INotificationManager.cs | 2 +- .../Notifications/INotificationService.cs | 2 ++ .../Notifications/UserNotification.cs | 2 ++ .../Persistence/IItemRepository.cs | 2 ++ .../Persistence/IUserDataRepository.cs | 2 ++ MediaBrowser.Controller/Playlists/Playlist.cs | 2 ++ .../Plugins/ILocalizablePlugin.cs | 22 ------------------- MediaBrowser.Controller/Providers/BookInfo.cs | 2 ++ .../Providers/DynamicImageResponse.cs | 2 ++ .../Providers/EpisodeInfo.cs | 2 ++ .../Providers/IExternalId.cs | 2 ++ .../Providers/IProviderManager.cs | 2 ++ .../Providers/ImageRefreshOptions.cs | 2 ++ MediaBrowser.Controller/Providers/ItemInfo.cs | 2 ++ .../Providers/ItemLookupInfo.cs | 2 ++ .../Providers/LocalImageInfo.cs | 2 ++ .../Providers/MetadataRefreshOptions.cs | 2 ++ .../Providers/MetadataResult.cs | 2 ++ .../Providers/MusicVideoInfo.cs | 2 ++ .../Providers/RemoteSearchQuery.cs | 2 ++ MediaBrowser.Controller/Providers/SongInfo.cs | 2 ++ .../QuickConnect/IQuickConnect.cs | 2 ++ .../Resolvers/BaseItemResolver.cs | 2 ++ .../Security/AuthenticationInfo.cs | 2 ++ .../Security/AuthenticationInfoQuery.cs | 2 ++ .../Security/IAuthenticationRepository.cs | 2 ++ .../Session/AuthenticationRequest.cs | 2 ++ .../Session/ISessionController.cs | 2 ++ .../Session/ISessionManager.cs | 2 ++ .../Session/SessionEventArgs.cs | 2 ++ .../Session/SessionInfo.cs | 2 ++ .../Sorting/AlphanumComparator.cs | 2 -- .../Sorting/IUserBaseItemComparer.cs | 2 ++ .../Sorting/SortExtensions.cs | 2 ++ .../Subtitles/ISubtitleManager.cs | 2 ++ .../Subtitles/ISubtitleProvider.cs | 2 ++ .../SubtitleDownloadFailureEventArgs.cs | 2 ++ .../Subtitles/SubtitleResponse.cs | 2 ++ .../Subtitles/SubtitleSearchRequest.cs | 2 ++ .../Sync/IHasDynamicAccess.cs | 2 ++ .../Sync/IServerSyncProvider.cs | 2 ++ MediaBrowser.Controller/Sync/ISyncProvider.cs | 2 ++ .../Sync/SyncedFileInfo.cs | 2 ++ .../SyncPlay/GroupMember.cs | 2 ++ .../GroupStates/AbstractGroupState.cs | 2 ++ .../SyncPlay/GroupStates/IdleGroupState.cs | 2 ++ .../SyncPlay/GroupStates/PausedGroupState.cs | 2 ++ .../SyncPlay/GroupStates/PlayingGroupState.cs | 2 ++ .../SyncPlay/GroupStates/WaitingGroupState.cs | 2 ++ .../SyncPlay/IGroupPlaybackRequest.cs | 2 ++ .../SyncPlay/IGroupState.cs | 2 ++ .../SyncPlay/IGroupStateContext.cs | 2 ++ .../SyncPlay/ISyncPlayManager.cs | 2 ++ .../AbstractPlaybackRequest.cs | 2 ++ .../PlaybackRequests/BufferGroupRequest.cs | 2 ++ .../IgnoreWaitGroupRequest.cs | 2 ++ .../MovePlaylistItemGroupRequest.cs | 2 ++ .../PlaybackRequests/NextItemGroupRequest.cs | 2 ++ .../PlaybackRequests/PauseGroupRequest.cs | 2 ++ .../PlaybackRequests/PingGroupRequest.cs | 2 ++ .../PlaybackRequests/PlayGroupRequest.cs | 2 ++ .../PreviousItemGroupRequest.cs | 2 ++ .../PlaybackRequests/QueueGroupRequest.cs | 2 ++ .../PlaybackRequests/ReadyGroupRequest.cs | 2 ++ .../RemoveFromPlaylistGroupRequest.cs | 2 ++ .../PlaybackRequests/SeekGroupRequest.cs | 2 ++ .../SetPlaylistItemGroupRequest.cs | 2 ++ .../SetRepeatModeGroupRequest.cs | 2 ++ .../SetShuffleModeGroupRequest.cs | 2 ++ .../SyncPlay/Queue/PlayQueueManager.cs | 2 ++ .../Images/EpisodeLocalImageProvider.cs | 4 ++++ 215 files changed, 406 insertions(+), 44 deletions(-) delete mode 100644 MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 1d4bbe61e..4ed15e1d5 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -600,7 +600,7 @@ namespace Jellyfin.Api.Controllers { foreach (var item in dto.Updates) { - _libraryMonitor.ReportFileSystemChanged(item.Path); + _libraryMonitor.ReportFileSystemChanged(item.Path ?? throw new ArgumentException("Item path can't be null.")); } return NoContent(); diff --git a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs index 4249a9a66..635e4eb3d 100644 --- a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs +++ b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs index ecdffa2eb..a56d3c822 100644 --- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs +++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs index 6729b9115..8c9d1baf8 100644 --- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs +++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs index a233c358e..68119cfed 100644 --- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Linq; using System.Threading; diff --git a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs index 8a8736427..b2b36c040 100644 --- a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index b2315bda4..26c64e0da 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs index 476992cbd..fa7aff647 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Channels/ChannelItemResult.cs b/MediaBrowser.Controller/Channels/ChannelItemResult.cs index cee7b2003..8e937852f 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemResult.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemResult.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs index 32469d4d7..53a73d62a 100644 --- a/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelSearchInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.Channels diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs index 2c0eadf95..01bf8d5c8 100644 --- a/MediaBrowser.Controller/Channels/IChannel.cs +++ b/MediaBrowser.Controller/Channels/IChannel.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index ddae7dbd3..4c5626338 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Channels/IHasCacheKey.cs b/MediaBrowser.Controller/Channels/IHasCacheKey.cs index bf895a0ec..9fae43033 100644 --- a/MediaBrowser.Controller/Channels/IHasCacheKey.cs +++ b/MediaBrowser.Controller/Channels/IHasCacheKey.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.Channels diff --git a/MediaBrowser.Controller/Channels/ISearchableChannel.cs b/MediaBrowser.Controller/Channels/ISearchableChannel.cs index b627ca1c2..b58446fc4 100644 --- a/MediaBrowser.Controller/Channels/ISearchableChannel.cs +++ b/MediaBrowser.Controller/Channels/ISearchableChannel.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs index 137f5d095..152c653dc 100644 --- a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs +++ b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs b/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs index 7e9bb28ed..0d837faca 100644 --- a/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs +++ b/MediaBrowser.Controller/Channels/InternalChannelItemQuery.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs index f6037d05e..94e7541f8 100644 --- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Collections/CollectionEvents.cs b/MediaBrowser.Controller/Collections/CollectionEvents.cs index ce59b4ada..821318ffc 100644 --- a/MediaBrowser.Controller/Collections/CollectionEvents.cs +++ b/MediaBrowser.Controller/Collections/CollectionEvents.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index a6991e2ea..46bc37e7f 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs index 43ad04dba..44e2c45dd 100644 --- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -1,3 +1,5 @@ +#nullable disable + using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Configuration; diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 8f0872dba..ef17c8fb3 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs index dc2d5a356..b51dc255c 100644 --- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs +++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 770c6dc2d..800f7a8bb 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 #nullable enable diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 142cebd0c..9bfead8b3 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 #nullable enable diff --git a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs index fe0465d0d..f06bbe4d0 100644 --- a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.Drawing diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index 596fcbc8c..204175ed5 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 #nullable enable diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 230a0af60..11e663301 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs index d3a2b4dbf..b036425ab 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Drawing/ImageStream.cs b/MediaBrowser.Controller/Drawing/ImageStream.cs index 46f58ec15..591cc53d1 100644 --- a/MediaBrowser.Controller/Drawing/ImageStream.cs +++ b/MediaBrowser.Controller/Drawing/ImageStream.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Drawing /// Gets or sets the stream. ///
/// The stream. - public Stream Stream { get; set; } + public Stream? Stream { get; set; } /// /// Gets or sets the format. diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs index 356783750..758e841a7 100644 --- a/MediaBrowser.Controller/Dto/DtoOptions.cs +++ b/MediaBrowser.Controller/Dto/DtoOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 988557f42..7f4bbead0 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Collections.Generic; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index 6a92200dd..f1944a7d3 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 8220464b3..4c2b7cb7c 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs index 20fad4cb0..1625c748a 100644 --- a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs index ac4dd1688..db60c3071 100644 --- a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs +++ b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.Entities.Audio diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 9a33ad9d7..610bce4f5 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 8a9bb12c7..6101d3016 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index f0c076108..b07d47ffd 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs index f4bd851e1..405284622 100644 --- a/MediaBrowser.Controller/Entities/AudioBook.cs +++ b/MediaBrowser.Controller/Entities/AudioBook.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 32ae15498..ca5213273 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs index 157ed8332..c39b18891 100644 --- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs +++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs @@ -1,3 +1,5 @@ +#nullable disable + #nullable enable #pragma warning disable CS1591 diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index ef5a5a734..1bd25042f 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Text.Json.Serialization; diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index 55945283c..3d0370248 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index bc5f38256..a86da29ce 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs index 3a34c668c..244cc00be 100644 --- a/MediaBrowser.Controller/Entities/Extensions.cs +++ b/MediaBrowser.Controller/Entities/Extensions.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Linq; using MediaBrowser.Common.Extensions; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index d74e6f9d8..a59f5c6e4 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 74a170204..7987f38a0 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/ICollectionFolder.cs b/MediaBrowser.Controller/Entities/ICollectionFolder.cs index b84a9fa6f..2304570fd 100644 --- a/MediaBrowser.Controller/Entities/ICollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/ICollectionFolder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/IHasAspectRatio.cs b/MediaBrowser.Controller/Entities/IHasAspectRatio.cs index d7d007668..3aeb7468f 100644 --- a/MediaBrowser.Controller/Entities/IHasAspectRatio.cs +++ b/MediaBrowser.Controller/Entities/IHasAspectRatio.cs @@ -1,3 +1,5 @@ +#nullable disable + namespace MediaBrowser.Controller.Entities { /// diff --git a/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs b/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs index 13226b234..14459624e 100644 --- a/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs +++ b/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs @@ -1,3 +1,5 @@ +#nullable disable + namespace MediaBrowser.Controller.Entities { /// diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index 0f612262a..98c3b3edf 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs index f747b5149..f80f7c304 100644 --- a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs +++ b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Model.LiveTv; diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs index 5444f1f52..64d769d5b 100644 --- a/MediaBrowser.Controller/Entities/IHasSeries.cs +++ b/MediaBrowser.Controller/Entities/IHasSeries.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs index 6a350212b..f317a02ff 100644 --- a/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs +++ b/MediaBrowser.Controller/Entities/IHasSpecialFeatures.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs index d1f6f2b7e..2bd9ded33 100644 --- a/MediaBrowser.Controller/Entities/IHasTrailers.cs +++ b/MediaBrowser.Controller/Entities/IHasTrailers.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 270217356..c06021029 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs index 5b96a5af6..b2d6a4609 100644 --- a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs index 570d8eec0..ea8555dbf 100644 --- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs +++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index 8e0f721e7..01c0a9339 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 507f400f1..74e84288d 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 8b67aaccc..64d60c2e9 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index b278a0142..f42e7723c 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index c4fcb0267..d9ff55362 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/PersonInfo.cs b/MediaBrowser.Controller/Entities/PersonInfo.cs index 4ff9b0955..fb79323f8 100644 --- a/MediaBrowser.Controller/Entities/PersonInfo.cs +++ b/MediaBrowser.Controller/Entities/PersonInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 0f82f742f..3312a0e3e 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Text.Json.Serialization; diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs index 50f1655f3..7e4ec1830 100644 --- a/MediaBrowser.Controller/Entities/Share.cs +++ b/MediaBrowser.Controller/Entities/Share.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.Entities diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 9018ddb75..ae1d10447 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 70663ef47..2724bd9b3 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 5b8168d3d..ad3e0fe8d 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 06a405121..ded825abc 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 9ae8ad708..b086b5906 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs index db63c42e4..f60359c01 100644 --- a/MediaBrowser.Controller/Entities/UserItemData.cs +++ b/MediaBrowser.Controller/Entities/UserItemData.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 7f7224ae0..e492740ed 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index fec83dd94..0dfde2766 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 78a64d8c9..15a4573c2 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 6320b01b8..723027a88 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index b2e4d307a..4d84a151a 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Events/IEventConsumer.cs b/MediaBrowser.Controller/Events/IEventConsumer.cs index 5c4ab5d8d..93005134a 100644 --- a/MediaBrowser.Controller/Events/IEventConsumer.cs +++ b/MediaBrowser.Controller/Events/IEventConsumer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace MediaBrowser.Controller.Events diff --git a/MediaBrowser.Controller/Events/IEventManager.cs b/MediaBrowser.Controller/Events/IEventManager.cs index a1f40b3a6..074e3f1fe 100644 --- a/MediaBrowser.Controller/Events/IEventManager.cs +++ b/MediaBrowser.Controller/Events/IEventManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace MediaBrowser.Controller.Events diff --git a/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs b/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs index 46d7e5a17..3a331ad00 100644 --- a/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs +++ b/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs @@ -1,4 +1,4 @@ -using Jellyfin.Data.Events; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Session; namespace MediaBrowser.Controller.Events.Session diff --git a/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs b/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs index aab19cc46..deeaaf55d 100644 --- a/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs +++ b/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs @@ -1,4 +1,4 @@ -using Jellyfin.Data.Events; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Session; namespace MediaBrowser.Controller.Events.Session diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs index b06046c05..0dd8b0dbf 100644 --- a/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs +++ b/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs @@ -1,4 +1,4 @@ -using Jellyfin.Data.Events; +using Jellyfin.Data.Events; using MediaBrowser.Model.Updates; namespace MediaBrowser.Controller.Events.Updates diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs index dfadc9f61..c1d503a7e 100644 --- a/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs +++ b/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs @@ -1,4 +1,4 @@ -using Jellyfin.Data.Events; +using Jellyfin.Data.Events; using MediaBrowser.Model.Updates; namespace MediaBrowser.Controller.Events.Updates diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs index 045a60027..7a9866834 100644 --- a/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs +++ b/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs @@ -1,4 +1,4 @@ -using Jellyfin.Data.Events; +using Jellyfin.Data.Events; using MediaBrowser.Model.Updates; namespace MediaBrowser.Controller.Events.Updates diff --git a/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs index 661ca066a..b078e06dc 100644 --- a/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs +++ b/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs @@ -1,4 +1,4 @@ -using Jellyfin.Data.Events; +using Jellyfin.Data.Events; using MediaBrowser.Model.Updates; namespace MediaBrowser.Controller.Events.Updates diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs index 182c8ef65..8441a3171 100644 --- a/MediaBrowser.Controller/Extensions/StringExtensions.cs +++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/IDisplayPreferencesManager.cs index be1d974a4..1678d5067 100644 --- a/MediaBrowser.Controller/IDisplayPreferencesManager.cs +++ b/MediaBrowser.Controller/IDisplayPreferencesManager.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using Jellyfin.Data.Entities; diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 6a65a8e47..094923842 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs index be57d6bca..1890dbb36 100644 --- a/MediaBrowser.Controller/IServerApplicationPaths.cs +++ b/MediaBrowser.Controller/IServerApplicationPaths.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Common.Configuration; diff --git a/MediaBrowser.Controller/Library/IIntroProvider.cs b/MediaBrowser.Controller/Library/IIntroProvider.cs index d45493d40..3bb1bd9a0 100644 --- a/MediaBrowser.Controller/Library/IIntroProvider.cs +++ b/MediaBrowser.Controller/Library/IIntroProvider.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 6d9b568da..782e15398 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/ILiveStream.cs b/MediaBrowser.Controller/Library/ILiveStream.cs index ff25be657..85d866de5 100644 --- a/MediaBrowser.Controller/Library/ILiveStream.cs +++ b/MediaBrowser.Controller/Library/ILiveStream.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Threading; diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 21c6ef2af..d3d85a056 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs index 027cc5b40..5fbfad881 100644 --- a/MediaBrowser.Controller/Library/IMetadataSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs index d12f008e7..5329841bf 100644 --- a/MediaBrowser.Controller/Library/IMusicManager.cs +++ b/MediaBrowser.Controller/Library/IMusicManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index c6a83e4dc..58499e853 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 6e267834b..c95b0ea32 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/IUserViewManager.cs b/MediaBrowser.Controller/Library/IUserViewManager.cs index 8d541e8b6..46004e42f 100644 --- a/MediaBrowser.Controller/Library/IUserViewManager.cs +++ b/MediaBrowser.Controller/Library/IUserViewManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/IntroInfo.cs b/MediaBrowser.Controller/Library/IntroInfo.cs index 283cc631c..90786786b 100644 --- a/MediaBrowser.Controller/Library/IntroInfo.cs +++ b/MediaBrowser.Controller/Library/IntroInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs index 1798a4fad..a37dc7af1 100644 --- a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs +++ b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 5f9aed341..0e2d8fb02 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs b/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs index 9581603f0..7bc8fa5ab 100644 --- a/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs +++ b/MediaBrowser.Controller/Library/LibraryManagerExtensions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs b/MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs index 884f9e773..41cfcae16 100644 --- a/MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs +++ b/MediaBrowser.Controller/Library/MetadataConfigurationExtensions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Common.Configuration; diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs index 6e79dc8dd..29bfeca09 100644 --- a/MediaBrowser.Controller/Library/NameExtensions.cs +++ b/MediaBrowser.Controller/Library/NameExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs index a2be3a42a..609336ec4 100644 --- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs +++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs index ac372bceb..2138fef58 100644 --- a/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs +++ b/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs @@ -1,4 +1,4 @@ -namespace MediaBrowser.Controller.Library +namespace MediaBrowser.Controller.Library { /// /// An event that occurs when playback is started. diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs index 5efdc6a48..8f42d3706 100644 --- a/MediaBrowser.Controller/Library/Profiler.cs +++ b/MediaBrowser.Controller/Library/Profiler.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Diagnostics; using System.Globalization; diff --git a/MediaBrowser.Controller/Library/SearchHintInfo.cs b/MediaBrowser.Controller/Library/SearchHintInfo.cs index 897c2b7f4..de7806adc 100644 --- a/MediaBrowser.Controller/Library/SearchHintInfo.cs +++ b/MediaBrowser.Controller/Library/SearchHintInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Library diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index a3aa6019e..8cbfc78aa 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace MediaBrowser.Controller.Library { @@ -12,7 +13,8 @@ namespace MediaBrowser.Controller.Library /// /// The day. /// List{DayOfWeek}. - public static DayOfWeek[] GetAirDays(string day) + [return: NotNullIfNotNull("day")] + public static DayOfWeek[] GetAirDays(string? day) { if (!string.IsNullOrEmpty(day)) { diff --git a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs index cd9109753..bfe433c97 100644 --- a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs +++ b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs index 166c4d77c..a55fd670d 100644 --- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Model.LiveTv; diff --git a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs index 038ff2eae..2bd4b20e8 100644 --- a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs +++ b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 54495c1c4..c28e0426b 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index 3ca1d165e..897f263f3 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index abca8f239..7dced9f5e 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index ec933caf3..51e56f4b5 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 43af495dd..d9634a731 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs index b62974904..eb3babc18 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs index 739978e7c..aa5eb59d1 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs index f9f559ee9..4a977c5cc 100644 --- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs index 69190694f..00135afa8 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs index 847c0ea8c..0b943c939 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs index 1343ecd98..1bb649a99 100644 --- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs index 1b8f41db6..728387c56 100644 --- a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #nullable enable #pragma warning disable CS1591 diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index aa5170617..e54dc967c 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs index 2759b314f..1c1a4417d 100644 --- a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs +++ b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.LiveTv diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 8c68b47dd..37ce35fc2 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -34,6 +34,7 @@ false true true + enable AllEnabledByDefault ../jellyfin.ruleset true diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2b5364775..97cb8d63b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index d47a689f4..1e13382b7 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index 1f3abe8f4..88de5b292 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs index fbc827534..c38e7ec3b 100644 --- a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs +++ b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.IO; diff --git a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs index 15a2580af..773547872 100644 --- a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs +++ b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 05dd1a69b..d3260280a 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 6ebf7f159..3fb2c47e1 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.IO; diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs index e7b4c8c15..044ba6d33 100644 --- a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.MediaEncoding diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index 227c5f258..aa5e2c403 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 89e01c08b..841e7b287 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 - namespace MediaBrowser.Controller.MediaEncoding { /// diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs index 2cb04bdc4..1dd8bcf31 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Model.Dlna; diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs index 93573e08e..2452b25ab 100644 --- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs +++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using Jellyfin.Data.Entities; diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 163a9c8f8..855467e8e 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs index 04b2e13e8..d15c6d318 100644 --- a/MediaBrowser.Controller/Net/IAuthService.cs +++ b/MediaBrowser.Controller/Net/IAuthService.cs @@ -1,5 +1,3 @@ -#nullable enable - using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index e87f3bca6..5e9fce550 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 #nullable enable diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs index c6347133a..f0d0b45a0 100644 --- a/MediaBrowser.Controller/Net/SecurityException.cs +++ b/MediaBrowser.Controller/Net/SecurityException.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; namespace MediaBrowser.Controller.Net diff --git a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs index be0b3ddc3..6f7ebf156 100644 --- a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs +++ b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + using MediaBrowser.Model.Net; namespace MediaBrowser.Controller.Net diff --git a/MediaBrowser.Controller/Notifications/INotificationManager.cs b/MediaBrowser.Controller/Notifications/INotificationManager.cs index 08d9bc12a..7caba1097 100644 --- a/MediaBrowser.Controller/Notifications/INotificationManager.cs +++ b/MediaBrowser.Controller/Notifications/INotificationManager.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Controller.Notifications /// Task. Task SendNotification(NotificationRequest request, CancellationToken cancellationToken); - Task SendNotification(NotificationRequest request, BaseItem relatedItem, CancellationToken cancellationToken); + Task SendNotification(NotificationRequest request, BaseItem? relatedItem, CancellationToken cancellationToken); /// /// Adds the parts. diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs index fa947220a..535c08795 100644 --- a/MediaBrowser.Controller/Notifications/INotificationService.cs +++ b/MediaBrowser.Controller/Notifications/INotificationService.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Threading; diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs index d768abfe7..4be0e09ae 100644 --- a/MediaBrowser.Controller/Notifications/UserNotification.cs +++ b/MediaBrowser.Controller/Notifications/UserNotification.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index ed473c749..56fb36af2 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 81ba513ce..6f5f02123 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Collections.Generic; using System.Threading; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 3c93cfc79..a80c11643 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs b/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs deleted file mode 100644 index bf15fe040..000000000 --- a/MediaBrowser.Controller/Plugins/ILocalizablePlugin.cs +++ /dev/null @@ -1,22 +0,0 @@ -#pragma warning disable CS1591 - -using System.IO; -using System.Reflection; - -namespace MediaBrowser.Controller.Plugins -{ - public interface ILocalizablePlugin - { - Stream GetDictionary(string culture); - } - - public static class LocalizablePluginHelper - { - public static Stream GetDictionary(Assembly assembly, string manifestPrefix, string culture) - { - // Find all dictionaries using GetManifestResourceNames, start start with the prefix - // Return the one for the culture if exists, otherwise return the default - return null; - } - } -} diff --git a/MediaBrowser.Controller/Providers/BookInfo.cs b/MediaBrowser.Controller/Providers/BookInfo.cs index cce0a25fc..3055c5d87 100644 --- a/MediaBrowser.Controller/Providers/BookInfo.cs +++ b/MediaBrowser.Controller/Providers/BookInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace MediaBrowser.Controller.Providers diff --git a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs index 006174be8..66fddb0e3 100644 --- a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs +++ b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs index a4c8dab7e..341bf6936 100644 --- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs +++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/IExternalId.cs b/MediaBrowser.Controller/Providers/IExternalId.cs index 5e38446bc..e2dbef2bc 100644 --- a/MediaBrowser.Controller/Providers/IExternalId.cs +++ b/MediaBrowser.Controller/Providers/IExternalId.cs @@ -1,3 +1,5 @@ +#nullable disable + using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 7bc56c82a..b4d91f396 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs index 9fc379f04..81a22affb 100644 --- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs index 3a97127ea..b8dd416a2 100644 --- a/MediaBrowser.Controller/Providers/ItemInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs index b777cc1d3..f16669a78 100644 --- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/LocalImageInfo.cs b/MediaBrowser.Controller/Providers/LocalImageInfo.cs index 41801862f..a8e70e6d0 100644 --- a/MediaBrowser.Controller/Providers/LocalImageInfo.cs +++ b/MediaBrowser.Controller/Providers/LocalImageInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index db0ef7072..5afc358ba 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index 98c7eadfe..8b0967a6e 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs index 0b927f6eb..322320abd 100644 --- a/MediaBrowser.Controller/Providers/MusicVideoInfo.cs +++ b/MediaBrowser.Controller/Providers/MusicVideoInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs index 9653bc1c4..d830231cf 100644 --- a/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs +++ b/MediaBrowser.Controller/Providers/RemoteSearchQuery.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Providers/SongInfo.cs b/MediaBrowser.Controller/Providers/SongInfo.cs index 58f76dca9..c90717a2e 100644 --- a/MediaBrowser.Controller/Providers/SongInfo.cs +++ b/MediaBrowser.Controller/Providers/SongInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs index 959a2d771..c4e709c24 100644 --- a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs +++ b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using MediaBrowser.Model.QuickConnect; diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs index a904c7424..e77593a03 100644 --- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs @@ -1,3 +1,5 @@ +#nullable disable + using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Controller/Security/AuthenticationInfo.cs b/MediaBrowser.Controller/Security/AuthenticationInfo.cs index efac9273e..b4b242f1b 100644 --- a/MediaBrowser.Controller/Security/AuthenticationInfo.cs +++ b/MediaBrowser.Controller/Security/AuthenticationInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs index c5f3da0b1..3af6a525c 100644 --- a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs +++ b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs index 883b74165..1dd69ccd8 100644 --- a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs +++ b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Model.Devices; diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs index 8c3ac58f2..647c75e66 100644 --- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs +++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index bc4ccd44c..6bc39d6f4 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 6c06dcad5..7eda49c60 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Session/SessionEventArgs.cs b/MediaBrowser.Controller/Session/SessionEventArgs.cs index 097e32eae..269fe7dc4 100644 --- a/MediaBrowser.Controller/Session/SessionEventArgs.cs +++ b/MediaBrowser.Controller/Session/SessionEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index d09852870..5da3783bf 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs index 70cb9eebe..4d9b98889 100644 --- a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs +++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs @@ -1,7 +1,5 @@ #pragma warning disable CS1591 -#nullable enable - using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs index 6d03d97ae..bd47db39a 100644 --- a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs +++ b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs @@ -1,3 +1,5 @@ +#nullable disable + using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.Sorting diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs index 88467814c..aa6ec513f 100644 --- a/MediaBrowser.Controller/Sorting/SortExtensions.cs +++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index 6d63286ef..9e661cbe4 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs index a633262de..326348d58 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs index ce8141219..c782f5796 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs index a86b05778..85b3e6fbd 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.IO; diff --git a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs index 7d3c20e8f..0f7c47e76 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs index e7395b136..3d3e44da0 100644 --- a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs +++ b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Threading; diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index c97fd7044..3891ac0a6 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs index 950cc73e8..ea20014c7 100644 --- a/MediaBrowser.Controller/Sync/ISyncProvider.cs +++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs index a626738fb..7eac52299 100644 --- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs +++ b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.Controller/SyncPlay/GroupMember.cs b/MediaBrowser.Controller/SyncPlay/GroupMember.cs index 5fb982e85..7e7e759a5 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupMember.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupMember.cs @@ -1,3 +1,5 @@ +#nullable disable + using MediaBrowser.Controller.Session; namespace MediaBrowser.Controller.SyncPlay diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs index e3de22db3..91a13fb28 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/AbstractGroupState.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay.PlaybackRequests; diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/IdleGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/IdleGroupState.cs index 12ce6c8f8..6b5a7cdf9 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/IdleGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/IdleGroupState.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay.PlaybackRequests; diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/PausedGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/PausedGroupState.cs index fba8ba9e2..b9786ddb0 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/PausedGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/PausedGroupState.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/PlayingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/PlayingGroupState.cs index 9797b247c..cb1cadf0b 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/PlayingGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/PlayingGroupState.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs index 507573653..a0c38b309 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/IGroupPlaybackRequest.cs b/MediaBrowser.Controller/SyncPlay/IGroupPlaybackRequest.cs index 201f29952..9045063ee 100644 --- a/MediaBrowser.Controller/SyncPlay/IGroupPlaybackRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/IGroupPlaybackRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/IGroupState.cs b/MediaBrowser.Controller/SyncPlay/IGroupState.cs index 95ee09985..0666a62a8 100644 --- a/MediaBrowser.Controller/SyncPlay/IGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/IGroupState.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay.PlaybackRequests; diff --git a/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs b/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs index aa263638a..de26c7d9e 100644 --- a/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs +++ b/MediaBrowser.Controller/SyncPlay/IGroupStateContext.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs index 1c954828c..a6999a12c 100644 --- a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/AbstractPlaybackRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/AbstractPlaybackRequest.cs index 4090f65b9..ef496c103 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/AbstractPlaybackRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/AbstractPlaybackRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/BufferGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/BufferGroupRequest.cs index 11cc99fcd..d188114c3 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/BufferGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/BufferGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/IgnoreWaitGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/IgnoreWaitGroupRequest.cs index 64ef791ed..464c81dfd 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/IgnoreWaitGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/IgnoreWaitGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/MovePlaylistItemGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/MovePlaylistItemGroupRequest.cs index 9cd8da566..be314e807 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/MovePlaylistItemGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/MovePlaylistItemGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/NextItemGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/NextItemGroupRequest.cs index e0ae0deb7..679076239 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/NextItemGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/NextItemGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PauseGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PauseGroupRequest.cs index 2869b35f7..7ee18a366 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PauseGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PauseGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PingGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PingGroupRequest.cs index 8ef3b2030..beab655c5 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PingGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PingGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PlayGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PlayGroupRequest.cs index 16f9b4087..05ff262c1 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PlayGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PlayGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PreviousItemGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PreviousItemGroupRequest.cs index 166ee0800..3e34b6ce4 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PreviousItemGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/PreviousItemGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/QueueGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/QueueGroupRequest.cs index d4af63b6d..0f91476de 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/QueueGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/QueueGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/ReadyGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/ReadyGroupRequest.cs index 74f01cbea..b1f0bd360 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/ReadyGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/ReadyGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs index 47c06c222..689145293 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SeekGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SeekGroupRequest.cs index ecaa689ae..196113374 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SeekGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SeekGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetPlaylistItemGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetPlaylistItemGroupRequest.cs index c3451703e..44df127a6 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetPlaylistItemGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetPlaylistItemGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Threading; using MediaBrowser.Controller.Session; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetRepeatModeGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetRepeatModeGroupRequest.cs index 51011672e..d250eab56 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetRepeatModeGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetRepeatModeGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetShuffleModeGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetShuffleModeGroupRequest.cs index d7b2504b4..5034e992e 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetShuffleModeGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/SetShuffleModeGroupRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Threading; using MediaBrowser.Controller.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs index fdec29417..b8ae9f3ff 100644 --- a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs +++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Linq; diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index 2d3b2d889..bc62ca4d5 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -42,6 +42,10 @@ namespace MediaBrowser.LocalMetadata.Images public IEnumerable GetImages(BaseItem item, IDirectoryService directoryService) { var parentPath = Path.GetDirectoryName(item.Path); + if (parentPath == null) + { + return Enumerable.Empty(); + } var parentPathFiles = directoryService.GetFiles(parentPath); From 4367b97a54f31233e242ef1afe6714b934ecf4d9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 May 2021 00:52:06 +0200 Subject: [PATCH 400/402] Fix build --- .../Library/ResolverHelper.cs | 5 +++++ MediaBrowser.Controller/Library/TVUtils.cs | 2 +- .../Providers/DirectoryService.cs | 19 +++++++++---------- .../Providers/IDirectoryService.cs | 2 +- MediaBrowser.LocalMetadata/BaseXmlProvider.cs | 2 +- .../Providers/BoxSetXmlProvider.cs | 2 +- .../Providers/PlaylistXmlProvider.cs | 2 +- .../Providers/AlbumNfoProvider.cs | 2 +- .../Providers/ArtistNfoProvider.cs | 2 +- .../Providers/EpisodeNfoProvider.cs | 2 +- .../Providers/SeasonNfoProvider.cs | 2 +- .../Providers/SeriesNfoProvider.cs | 2 +- 12 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index b1a2e9284..1d9b44874 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -44,6 +44,11 @@ namespace Emby.Server.Implementations.Library // Make sure DateCreated and DateModified have values var fileInfo = directoryService.GetFile(item.Path); + if (fileInfo == null) + { + throw new FileNotFoundException("Can't find item path.", item.Path); + } + SetDateCreated(item, fileInfo); EnsureName(item, fileInfo); diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index 8cbfc78aa..968338dc6 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Library /// The day. /// List{DayOfWeek}. [return: NotNullIfNotNull("day")] - public static DayOfWeek[] GetAirDays(string? day) + public static DayOfWeek[]? GetAirDays(string? day) { if (!string.IsNullOrEmpty(day)) { diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index 5c92069b4..291a26883 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -43,18 +43,17 @@ namespace MediaBrowser.Controller.Providers return list; } - public FileSystemMetadata GetFile(string path) + public FileSystemMetadata? GetFile(string path) { - var result = _fileCache.GetOrAdd(path, p => + if (!_fileCache.TryGetValue(path, out var result)) { - var file = _fileSystem.GetFileInfo(p); - return file != null && file.Exists ? file : null; - }); - - if (result == null) - { - // lets not store null results in the cache - _fileCache.TryRemove(path, out _); + var file = _fileSystem.GetFileInfo(path); + var res = file != null && file.Exists ? file : null; + if (res != null) + { + result = res; + _fileCache.TryAdd(path, result); + } } return result; diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs index f06481c7a..9cee06a4c 100644 --- a/MediaBrowser.Controller/Providers/IDirectoryService.cs +++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Providers List GetFiles(string path); - FileSystemMetadata GetFile(string path); + FileSystemMetadata? GetFile(string path); IReadOnlyList GetFilePaths(string path); diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs index b036a2cc8..9f8e921b4 100644 --- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs @@ -91,7 +91,7 @@ namespace MediaBrowser.LocalMetadata /// Item inf. /// Instance of the interface. /// The file system metadata. - protected abstract FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService); + protected abstract FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService); /// public bool HasChanged(BaseItem item, IDirectoryService directoryService) diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs index cc705a9df..ea123bbf2 100644 --- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.LocalMetadata.Providers } /// - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) + protected override FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService) { return directoryService.GetFile(Path.Combine(info.Path, "collection.xml")); } diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs index 36f3048ad..899035652 100644 --- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.LocalMetadata.Providers } /// - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) + protected override FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService) { return directoryService.GetFile(PlaylistXmlSaver.GetSavePath(info.Path)); } diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs index bd557d783..f7a85a07e 100644 --- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.XbmcMetadata.Providers } /// - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) + protected override FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService) => directoryService.GetFile(Path.Combine(info.Path, "album.nfo")); } } diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs index 54bb83114..2b563b7fa 100644 --- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.XbmcMetadata.Providers } /// - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) + protected override FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService) => directoryService.GetFile(Path.Combine(info.Path, "artist.nfo")); } } diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs index 64b208345..f6c5877b4 100644 --- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.XbmcMetadata.Providers } /// - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) + protected override FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService) { var path = Path.ChangeExtension(info.Path, ".nfo"); diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs index 97220cf7e..d01abadf6 100644 --- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.XbmcMetadata.Providers } /// - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) + protected override FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService) => directoryService.GetFile(Path.Combine(info.Path, "season.nfo")); } } diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs index 9a9b94123..002b94463 100644 --- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.XbmcMetadata.Providers } /// - protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService) + protected override FileSystemMetadata? GetXmlFile(ItemInfo info, IDirectoryService directoryService) => directoryService.GetFile(Path.Combine(info.Path, "tvshow.nfo")); } } From 06caee28b7f81fb6898a771df5a5064fdf94891d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 May 2021 14:43:50 +0200 Subject: [PATCH 401/402] Enable nullable reference types for Emby.Dlna --- Emby.Dlna/Configuration/DlnaOptions.cs | 2 ++ Emby.Dlna/ConfigurationExtension.cs | 1 - .../ContentDirectory/ContentDirectoryService.cs | 2 ++ Emby.Dlna/ContentDirectory/ControlHandler.cs | 2 ++ Emby.Dlna/ControlRequest.cs | 2 ++ Emby.Dlna/ControlResponse.cs | 2 ++ Emby.Dlna/Didl/DidlBuilder.cs | 2 ++ Emby.Dlna/Didl/StringWriterWithEncoding.cs | 2 +- Emby.Dlna/DlnaConfigurationFactory.cs | 1 - Emby.Dlna/DlnaManager.cs | 2 ++ Emby.Dlna/Emby.Dlna.csproj | 1 + Emby.Dlna/EventSubscriptionResponse.cs | 2 ++ Emby.Dlna/Eventing/DlnaEventManager.cs | 2 ++ Emby.Dlna/Eventing/EventSubscription.cs | 2 ++ Emby.Dlna/Main/DlnaEntryPoint.cs | 2 ++ Emby.Dlna/PlayTo/Device.cs | 2 ++ Emby.Dlna/PlayTo/DeviceInfo.cs | 2 ++ Emby.Dlna/PlayTo/MediaChangedEventArgs.cs | 2 ++ Emby.Dlna/PlayTo/PlayToController.cs | 2 ++ Emby.Dlna/PlayTo/PlayToManager.cs | 2 ++ Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs | 2 ++ Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs | 2 ++ Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs | 2 ++ Emby.Dlna/PlayTo/PlaylistItem.cs | 2 ++ Emby.Dlna/PlayTo/PlaylistItemFactory.cs | 2 ++ Emby.Dlna/PlayTo/SsdpHttpClient.cs | 2 ++ Emby.Dlna/PlayTo/TransportCommands.cs | 14 +++++++------- Emby.Dlna/PlayTo/uBaseObject.cs | 2 ++ Emby.Dlna/Server/DescriptionXmlBuilder.cs | 3 ++- Emby.Dlna/Service/BaseControlHandler.cs | 4 ++-- Emby.Dlna/Ssdp/DeviceDiscovery.cs | 2 ++ Emby.Dlna/Ssdp/SsdpExtensions.cs | 6 +++--- 32 files changed, 64 insertions(+), 16 deletions(-) diff --git a/Emby.Dlna/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs index e63a85860..5ceeb5530 100644 --- a/Emby.Dlna/Configuration/DlnaOptions.cs +++ b/Emby.Dlna/Configuration/DlnaOptions.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 namespace Emby.Dlna.Configuration diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs index fc02e1751..3ca43052a 100644 --- a/Emby.Dlna/ConfigurationExtension.cs +++ b/Emby.Dlna/ConfigurationExtension.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CS1591 using Emby.Dlna.Configuration; diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs index 2f3107450..7b8c50440 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 90ba601b4..27c5b2268 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Globalization; diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs index 4ea4e4e48..8ee6325e9 100644 --- a/Emby.Dlna/ControlRequest.cs +++ b/Emby.Dlna/ControlRequest.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.IO; diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs index d827eef26..a7f2d4a73 100644 --- a/Emby.Dlna/ControlResponse.cs +++ b/Emby.Dlna/ControlResponse.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index c66bdbf22..2982ce97e 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs index 2b86ea333..b66f53ece 100644 --- a/Emby.Dlna/Didl/StringWriterWithEncoding.cs +++ b/Emby.Dlna/Didl/StringWriterWithEncoding.cs @@ -9,7 +9,7 @@ namespace Emby.Dlna.Didl { public class StringWriterWithEncoding : StringWriter { - private readonly Encoding _encoding; + private readonly Encoding? _encoding; public StringWriterWithEncoding() { diff --git a/Emby.Dlna/DlnaConfigurationFactory.cs b/Emby.Dlna/DlnaConfigurationFactory.cs index 4c6ca869a..6cc6b73a0 100644 --- a/Emby.Dlna/DlnaConfigurationFactory.cs +++ b/Emby.Dlna/DlnaConfigurationFactory.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 5f2be07cf..a1b106704 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 480621dd7..a40578e40 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -21,6 +21,7 @@ false true true + enable diff --git a/Emby.Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs index 1b1bd426c..8c82dcbf6 100644 --- a/Emby.Dlna/EventSubscriptionResponse.cs +++ b/Emby.Dlna/EventSubscriptionResponse.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/Emby.Dlna/Eventing/DlnaEventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs index ff81e83b5..2e672b886 100644 --- a/Emby.Dlna/Eventing/DlnaEventManager.cs +++ b/Emby.Dlna/Eventing/DlnaEventManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/Eventing/EventSubscription.cs b/Emby.Dlna/Eventing/EventSubscription.cs index 40d73ee0e..4fd7f8169 100644 --- a/Emby.Dlna/Eventing/EventSubscription.cs +++ b/Emby.Dlna/Eventing/EventSubscription.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index bdfe430cf..0309926ab 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index abd99bbc3..5fa1fd589 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs index d3daab9e0..2acfff4eb 100644 --- a/Emby.Dlna/PlayTo/DeviceInfo.cs +++ b/Emby.Dlna/PlayTo/DeviceInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs index dabd079af..2bc4d8cc2 100644 --- a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs +++ b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index ee09cc65a..1e6a5fadb 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index f2f526221..35bf5927c 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs index d14617c8a..c7d2b28df 100644 --- a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs index 3f8d55263..f8a14f411 100644 --- a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs index deeb47918..6661f92ac 100644 --- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/PlaylistItem.cs b/Emby.Dlna/PlayTo/PlaylistItem.cs index 85846166c..5056e69ae 100644 --- a/Emby.Dlna/PlayTo/PlaylistItem.cs +++ b/Emby.Dlna/PlayTo/PlaylistItem.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using MediaBrowser.Model.Dlna; diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs index e28840a89..657491303 100644 --- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System.IO; diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index d9f1ce490..f14f73bb6 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs index cbcf66e45..b58669355 100644 --- a/Emby.Dlna/PlayTo/TransportCommands.cs +++ b/Emby.Dlna/PlayTo/TransportCommands.cs @@ -46,7 +46,7 @@ namespace Emby.Dlna.PlayTo { var serviceAction = new ServiceAction { - Name = container.GetValue(UPnpNamespaces.Svc + "name"), + Name = container.GetValue(UPnpNamespaces.Svc + "name") ?? string.Empty, }; var argumentList = serviceAction.ArgumentList; @@ -68,9 +68,9 @@ namespace Emby.Dlna.PlayTo return new Argument { - Name = container.GetValue(UPnpNamespaces.Svc + "name"), - Direction = container.GetValue(UPnpNamespaces.Svc + "direction"), - RelatedStateVariable = container.GetValue(UPnpNamespaces.Svc + "relatedStateVariable") + Name = container.GetValue(UPnpNamespaces.Svc + "name") ?? string.Empty, + Direction = container.GetValue(UPnpNamespaces.Svc + "direction") ?? string.Empty, + RelatedStateVariable = container.GetValue(UPnpNamespaces.Svc + "relatedStateVariable") ?? string.Empty }; } @@ -89,8 +89,8 @@ namespace Emby.Dlna.PlayTo return new StateVariable { - Name = container.GetValue(UPnpNamespaces.Svc + "name"), - DataType = container.GetValue(UPnpNamespaces.Svc + "dataType"), + Name = container.GetValue(UPnpNamespaces.Svc + "name") ?? string.Empty, + DataType = container.GetValue(UPnpNamespaces.Svc + "dataType") ?? string.Empty, AllowedValues = allowedValues }; } @@ -166,7 +166,7 @@ namespace Emby.Dlna.PlayTo return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString); } - private string BuildArgumentXml(Argument argument, string value, string commandParameter = "") + private string BuildArgumentXml(Argument argument, string? value, string commandParameter = "") { var state = StateVariables.FirstOrDefault(a => string.Equals(a.Name, argument.RelatedStateVariable, StringComparison.OrdinalIgnoreCase)); diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs index 0d9478e42..02d2da58d 100644 --- a/Emby.Dlna/PlayTo/uBaseObject.cs +++ b/Emby.Dlna/PlayTo/uBaseObject.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs index 09525aae4..3f3dfccd3 100644 --- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs +++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs @@ -250,7 +250,8 @@ namespace Emby.Dlna.Server url = _serverAddress.TrimEnd('/') + "/dlna/" + _serverUdn + "/" + url.TrimStart('/'); - return SecurityElement.Escape(url); + // TODO: @bond remove null-coalescing operator when https://github.com/dotnet/runtime/pull/52442 is merged/released + return SecurityElement.Escape(url) ?? string.Empty; } private IEnumerable GetIcons() diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index fda8346f9..904c23d99 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -47,7 +47,7 @@ namespace Emby.Dlna.Service private async Task ProcessControlRequestInternalAsync(ControlRequest request) { - ControlRequestInfo requestInfo = null; + ControlRequestInfo? requestInfo = null; using (var streamReader = new StreamReader(request.InputXml, Encoding.UTF8)) { @@ -151,7 +151,7 @@ namespace Emby.Dlna.Service private async Task ParseBodyTagAsync(XmlReader reader) { - string namespaceURI = null, localName = null; + string? namespaceURI = null, localName = null; await reader.MoveToContentAsync().ConfigureAwait(false); await reader.ReadAsync().ConfigureAwait(false); diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index d9c6a93c7..391dda147 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs @@ -1,3 +1,5 @@ +#nullable disable + #pragma warning disable CS1591 using System; diff --git a/Emby.Dlna/Ssdp/SsdpExtensions.cs b/Emby.Dlna/Ssdp/SsdpExtensions.cs index e7a52f168..d00eb02b4 100644 --- a/Emby.Dlna/Ssdp/SsdpExtensions.cs +++ b/Emby.Dlna/Ssdp/SsdpExtensions.cs @@ -7,21 +7,21 @@ namespace Emby.Dlna.Ssdp { public static class SsdpExtensions { - public static string GetValue(this XElement container, XName name) + public static string? GetValue(this XElement container, XName name) { var node = container.Element(name); return node?.Value; } - public static string GetAttributeValue(this XElement container, XName name) + public static string? GetAttributeValue(this XElement container, XName name) { var node = container.Attribute(name); return node?.Value; } - public static string GetDescendantValue(this XElement container, XName name) + public static string? GetDescendantValue(this XElement container, XName name) => container.Descendants(name).FirstOrDefault()?.Value; } } From d21d1978a3fce65a0af144d4293ce92bbf1f5b86 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Fri, 7 May 2021 21:09:05 +0200 Subject: [PATCH 402/402] Disable automation CI on issues --- .github/workflows/automation.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml index a203e6695..2529d8099 100644 --- a/.github/workflows/automation.yml +++ b/.github/workflows/automation.yml @@ -2,8 +2,6 @@ name: Automation on: pull_request: - issues: - issue_comment: jobs: main: