diff options
| -rw-r--r-- | Emby.Server.Implementations/Library/LibraryManager.cs | 5 | ||||
| -rw-r--r-- | Emby.Server.Implementations/Localization/Core/he.json | 3 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/DlnaServerController.cs | 7 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/ImageController.cs | 11 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Movies/Movie.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Episode.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Series.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 45 | ||||
| -rw-r--r-- | MediaBrowser.Model/Drawing/ImageFormatExtensions.cs | 27 | ||||
| -rw-r--r-- | MediaBrowser.Model/Net/MimeTypes.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Providers/Manager/ItemImageProvider.cs | 15 | ||||
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | tests/Jellyfin.Model.Tests/Drawing/ImageFormatExtensionsTests.cs | 33 | ||||
| -rw-r--r-- | tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs | 1 |
15 files changed, 121 insertions, 41 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 464a621cf..bd0c178fd 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -3007,7 +3007,10 @@ namespace Emby.Server.Implementations.Library } } - CreateItems(personsToSave, null, CancellationToken.None); + if (personsToSave.Count > 0) + { + CreateItems(personsToSave, null, CancellationToken.None); + } } private void StartScanInBackground() diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 5e299ea0e..e32ab4ca8 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -119,5 +119,6 @@ "Undefined": "לא מוגדר", "Forced": "כפוי", "Default": "ברירת מחדל", - "TaskOptimizeDatabase": "מיטוב מסד נתונים" + "TaskOptimizeDatabase": "מיטוב מסד נתונים", + "TaskOptimizeDatabaseDescription": "דוחס את מסד הנתונים ומוריד את שטח האחסון שבשימוש. הרצה של פעולה זו לאחר סריקת הספרייה או שינויים אחרים שמשפיעים על מסד הנתונים יכולה לשפר ביצועים." } diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 4e8c01577..b1c576c33 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -9,6 +9,7 @@ using Emby.Dlna.Main; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using MediaBrowser.Controller.Dlna; +using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -337,11 +338,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var contentType = "image/" + Path.GetExtension(fileName) - .TrimStart('.') - .ToLowerInvariant(); - - return File(icon.Stream, contentType); + return File(icon.Stream, MimeTypes.GetMimeType(fileName)); } private string GetAbsoluteUri() diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 86933074d..e72589cfa 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1878,8 +1878,8 @@ namespace Jellyfin.Api.Controllers if (!supportsWebP) { var userAgent = Request.Headers[HeaderNames.UserAgent].ToString(); - if (userAgent.IndexOf("crosswalk", StringComparison.OrdinalIgnoreCase) != -1 && - userAgent.IndexOf("android", StringComparison.OrdinalIgnoreCase) != -1) + if (userAgent.Contains("crosswalk", StringComparison.OrdinalIgnoreCase) + && userAgent.Contains("android", StringComparison.OrdinalIgnoreCase)) { supportsWebP = true; } @@ -1905,10 +1905,7 @@ namespace Jellyfin.Api.Controllers private bool SupportsFormat(IReadOnlyCollection<string> requestAcceptTypes, string acceptParam, ImageFormat format, bool acceptAll) { - var normalized = format.ToString().ToLowerInvariant(); - var mimeType = "image/" + normalized; - - if (requestAcceptTypes.Contains(mimeType)) + if (requestAcceptTypes.Contains(format.GetMimeType())) { return true; } @@ -1918,6 +1915,8 @@ namespace Jellyfin.Api.Controllers return true; } + // Review if this should be jpeg, jpg or both for ImageFormat.Jpg + var normalized = format.ToString().ToLowerInvariant(); return string.Equals(acceptParam, normalized, StringComparison.OrdinalIgnoreCase); } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 6b93d8d87..882abc927 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.Entities.Movies public override bool SupportsPeople => true; /// <inheritdoc /> + [JsonIgnore] public IReadOnlyList<BaseItem> LocalTrailers => GetExtras() .Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer) .ToArray(); diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index dfaf03fda..b9455721e 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -26,6 +26,7 @@ namespace MediaBrowser.Controller.Entities.Movies .ToArray(); /// <inheritdoc /> + [JsonIgnore] public IReadOnlyList<BaseItem> LocalTrailers => GetExtras() .Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer) .ToArray(); diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index dcc752f8c..df47daedf 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -21,6 +21,7 @@ namespace MediaBrowser.Controller.Entities.TV public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries { /// <inheritdoc /> + [JsonIgnore] public IReadOnlyList<BaseItem> LocalTrailers => GetExtras() .Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer) .ToArray(); diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index bdadc2775..a3c4a81fd 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -50,6 +50,7 @@ namespace MediaBrowser.Controller.Entities.TV public override bool SupportsPeople => true; /// <inheritdoc /> + [JsonIgnore] public IReadOnlyList<BaseItem> LocalTrailers => GetExtras() .Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer) .ToArray(); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 9c96d2472..cbf904061 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -11,6 +11,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; @@ -1318,7 +1319,7 @@ namespace MediaBrowser.Controller.MediaEncoding { string[] valid_h264_qsv = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; - if (valid_h264_qsv.Contains(encodingOptions.EncoderPreset, StringComparer.OrdinalIgnoreCase)) + if (valid_h264_qsv.Contains(encodingOptions.EncoderPreset, StringComparison.OrdinalIgnoreCase)) { param += " -preset " + encodingOptions.EncoderPreset; } @@ -1669,7 +1670,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Source and target codecs must match if (string.IsNullOrEmpty(videoStream.Codec) - || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase)) + || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { return false; } @@ -1687,7 +1688,7 @@ namespace MediaBrowser.Controller.MediaEncoding var requestedProfile = requestedProfiles[0]; // strip spaces because they may be stripped out on the query string as well if (!string.IsNullOrEmpty(videoStream.Profile) - && !requestedProfiles.Contains(videoStream.Profile.Replace(" ", string.Empty, StringComparison.Ordinal), StringComparer.OrdinalIgnoreCase)) + && !requestedProfiles.Contains(videoStream.Profile.Replace(" ", string.Empty, StringComparison.Ordinal), StringComparison.OrdinalIgnoreCase)) { var currentScore = GetVideoProfileScore(videoStream.Codec, videoStream.Profile); var requestedScore = GetVideoProfileScore(videoStream.Codec, requestedProfile); @@ -1794,7 +1795,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Source and target codecs must match if (string.IsNullOrEmpty(audioStream.Codec) - || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase)) + || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparison.OrdinalIgnoreCase)) { return false; } @@ -4302,11 +4303,19 @@ namespace MediaBrowser.Controller.MediaEncoding var decoderName = decoderPrefix + '_' + decoderSuffix; - var isCodecAvailable = _mediaEncoder.SupportsDecoder(decoderName) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase); + var isCodecAvailable = _mediaEncoder.SupportsDecoder(decoderName) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase); if (bitDepth == 10 && isCodecAvailable) { - if ((options.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase) && !options.EnableDecodingColorDepth10Hevc) - || (options.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase) && !options.EnableDecodingColorDepth10Vp9)) + if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase) + && options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase) + && !options.EnableDecodingColorDepth10Hevc) + { + return null; + } + + if (string.Equals(videoCodec, "vp9", StringComparison.OrdinalIgnoreCase) + && options.HardwareDecodingCodecs.Contains("vp9", StringComparison.OrdinalIgnoreCase) + && !options.EnableDecodingColorDepth10Vp9) { return null; } @@ -4344,15 +4353,23 @@ namespace MediaBrowser.Controller.MediaEncoding var isCudaSupported = (isLinux || isWindows) && IsCudaFullSupported(); var isQsvSupported = (isLinux || isWindows) && _mediaEncoder.SupportsHwaccel("qsv"); var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox"); - var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase); + var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase); // Set the av1 codec explicitly to trigger hw accelerator, otherwise libdav1d will be used. var isAv1 = string.Equals(videoCodec, "av1", StringComparison.OrdinalIgnoreCase); if (bitDepth == 10 && isCodecAvailable) { - if ((options.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase) && !options.EnableDecodingColorDepth10Hevc) - || (options.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase) && !options.EnableDecodingColorDepth10Vp9)) + if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase) + && options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase) + && !options.EnableDecodingColorDepth10Hevc) + { + return null; + } + + if (string.Equals(videoCodec, "vp9", StringComparison.OrdinalIgnoreCase) + && options.HardwareDecodingCodecs.Contains("vp9", StringComparison.OrdinalIgnoreCase) + && !options.EnableDecodingColorDepth10Vp9) { return null; } @@ -5072,12 +5089,12 @@ namespace MediaBrowser.Controller.MediaEncoding // Transcoding to 2ch ac3 almost always causes a playback failure // Keep it in the supported codecs list, but shift it to the end of the list so that if transcoding happens, another codec is used var shiftAudioCodecs = new[] { "ac3", "eac3" }; - if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparer.OrdinalIgnoreCase))) + if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparison.OrdinalIgnoreCase))) { return; } - while (shiftAudioCodecs.Contains(audioCodecs[0], StringComparer.OrdinalIgnoreCase)) + while (shiftAudioCodecs.Contains(audioCodecs[0], StringComparison.OrdinalIgnoreCase)) { var removed = shiftAudioCodecs[0]; audioCodecs.RemoveAt(0); @@ -5100,12 +5117,12 @@ namespace MediaBrowser.Controller.MediaEncoding } var shiftVideoCodecs = new[] { "hevc", "h265" }; - if (videoCodecs.All(i => shiftVideoCodecs.Contains(i, StringComparer.OrdinalIgnoreCase))) + if (videoCodecs.All(i => shiftVideoCodecs.Contains(i, StringComparison.OrdinalIgnoreCase))) { return; } - while (shiftVideoCodecs.Contains(videoCodecs[0], StringComparer.OrdinalIgnoreCase)) + while (shiftVideoCodecs.Contains(videoCodecs[0], StringComparison.OrdinalIgnoreCase)) { var removed = shiftVideoCodecs[0]; videoCodecs.RemoveAt(0); diff --git a/MediaBrowser.Model/Drawing/ImageFormatExtensions.cs b/MediaBrowser.Model/Drawing/ImageFormatExtensions.cs new file mode 100644 index 000000000..68a5c2534 --- /dev/null +++ b/MediaBrowser.Model/Drawing/ImageFormatExtensions.cs @@ -0,0 +1,27 @@ +using System.ComponentModel; +using System.Net.Mime; + +namespace MediaBrowser.Model.Drawing; + +/// <summary> +/// Extension class for the <see cref="ImageFormat" /> enum. +/// </summary> +public static class ImageFormatExtensions +{ + /// <summary> + /// Returns the correct mime type for this <see cref="ImageFormat" />. + /// </summary> + /// <param name="format">This <see cref="ImageFormat" />.</param> + /// <exception cref="InvalidEnumArgumentException">The <paramref name="format"/> is an invalid enumeration value.</exception> + /// <returns>The correct mime type for this <see cref="ImageFormat" />.</returns> + public static string GetMimeType(this ImageFormat format) + => format switch + { + ImageFormat.Bmp => "image/bmp", + ImageFormat.Gif => MediaTypeNames.Image.Gif, + ImageFormat.Jpg => MediaTypeNames.Image.Jpeg, + ImageFormat.Png => "image/png", + ImageFormat.Webp => "image/webp", + _ => throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(ImageFormat)) + }; +} diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index ee8451853..3b03466e9 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -116,7 +116,6 @@ namespace MediaBrowser.Model.Net { "audio/x-wavpack", ".wv" }, // Type image - { "image/jpg", ".jpg" }, { "image/jpeg", ".jpg" }, { "image/x-png", ".png" }, diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index b1d73c4c4..0d1bdec58 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; @@ -172,7 +173,13 @@ namespace MediaBrowser.Providers.Manager if (response.HasImage) { - if (!string.IsNullOrEmpty(response.Path)) + if (string.IsNullOrEmpty(response.Path)) + { + var mimeType = response.Format.GetMimeType(); + + await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); + } + else { if (response.Protocol == MediaProtocol.Http) { @@ -195,12 +202,6 @@ namespace MediaBrowser.Providers.Manager await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); } } - else - { - var mimeType = "image/" + response.Format.ToString().ToLowerInvariant(); - - await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); - } downloadedImages.Add(imageType); result.UpdateType |= ItemUpdateType.ImageUpdate; @@ -85,7 +85,7 @@ These instructions will help you get set up with a local development environment Before the project can be built, you must first install the [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet) on your system. -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). +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 6 development will work, but two options are recent versions of [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2022) and [Visual Studio Code](https://code.visualstudio.com/Download). [ffmpeg](https://github.com/jellyfin/jellyfin-ffmpeg) will also need to be installed. @@ -138,10 +138,10 @@ A second option is to build the project and then run the resulting executable fi 1. Build the project - ```bash - dotnet build # Build the project - cd bin/Debug/net5.0 # Change into the build output directory - ``` +```bash +dotnet build # Build the project +cd Jellyfin.Server/bin/Debug/net6.0 # Change into the build output directory +``` 2. Execute the build output. On Linux, Mac, etc. use `./jellyfin` and on Windows use `jellyfin.exe`. diff --git a/tests/Jellyfin.Model.Tests/Drawing/ImageFormatExtensionsTests.cs b/tests/Jellyfin.Model.Tests/Drawing/ImageFormatExtensionsTests.cs new file mode 100644 index 000000000..7c3a7ff6c --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Drawing/ImageFormatExtensionsTests.cs @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel; +using MediaBrowser.Model.Drawing; +using Xunit; + +namespace Jellyfin.Model.Drawing; + +public static class ImageFormatExtensionsTests +{ + private static TheoryData<ImageFormat> GetAllImageFormats() + { + var theoryTypes = new TheoryData<ImageFormat>(); + foreach (var x in Enum.GetValues<ImageFormat>()) + { + theoryTypes.Add(x); + } + + return theoryTypes; + } + + [Theory] + [MemberData(nameof(GetAllImageFormats))] + public static void GetMimeType_Valid_Valid(ImageFormat format) + => Assert.Null(Record.Exception(() => format.GetMimeType())); + + [Theory] + [InlineData((ImageFormat)int.MinValue)] + [InlineData((ImageFormat)int.MaxValue)] + [InlineData((ImageFormat)(-1))] + [InlineData((ImageFormat)5)] + public static void GetMimeType_Valid_ThrowsInvalidEnumArgumentException(ImageFormat format) + => Assert.Throws<InvalidEnumArgumentException>(() => format.GetMimeType()); +} diff --git a/tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs b/tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs index 7b50c54b0..cbab455f0 100644 --- a/tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs +++ b/tests/Jellyfin.Model.Tests/Net/MimeTypesTests.cs @@ -124,7 +124,6 @@ namespace Jellyfin.Model.Tests.Net [InlineData("font/woff2", ".woff2")] [InlineData("image/bmp", ".bmp")] [InlineData("image/gif", ".gif")] - [InlineData("image/jpg", ".jpg")] [InlineData("image/jpeg", ".jpg")] [InlineData("image/png", ".png")] [InlineData("image/svg+xml", ".svg")] |
