aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Directory.Build.props21
-rw-r--r--src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj15
-rw-r--r--src/Jellyfin.Drawing.Skia/SkiaEncoder.cs163
-rw-r--r--src/Jellyfin.Drawing.Skia/SkiaHelper.cs8
-rw-r--r--src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs16
-rw-r--r--src/Jellyfin.Drawing/ImageProcessor.cs66
-rw-r--r--src/Jellyfin.Drawing/Jellyfin.Drawing.csproj11
-rw-r--r--src/Jellyfin.Drawing/NullImageEncoder.cs6
-rw-r--r--src/Jellyfin.Extensions/Jellyfin.Extensions.csproj12
-rw-r--r--src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs2
-rw-r--r--src/Jellyfin.Extensions/StreamExtensions.cs10
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj11
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs67
-rw-r--r--src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj11
14 files changed, 207 insertions, 212 deletions
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
new file mode 100644
index 000000000..ac2726ed5
--- /dev/null
+++ b/src/Directory.Build.props
@@ -0,0 +1,21 @@
+<Project>
+ <!-- Sets defaults for all projects -->
+
+ <Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
+
+ <!-- Code Analyzers -->
+ <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="IDisposableAnalyzers">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index 034691322..3c417f8ff 100644
--- a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -18,11 +18,11 @@
<ItemGroup>
<PackageReference Include="BlurHashSharp" />
<PackageReference Include="BlurHashSharp.SkiaSharp" />
+ <PackageReference Include="HarfBuzzSharp.NativeAssets.Linux" />
<PackageReference Include="SkiaSharp" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" />
- <PackageReference Include="SkiaSharp.Svg" />
<PackageReference Include="SkiaSharp.HarfBuzz" />
- <PackageReference Include="HarfBuzzSharp.NativeAssets.Linux" />
+ <PackageReference Include="Svg.Skia" />
</ItemGroup>
<ItemGroup>
@@ -31,15 +31,4 @@
<ProjectReference Include="..\..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup>
- <!-- Code analysers-->
- <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
- </PackageReference>
- <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
- </ItemGroup>
-
</Project>
diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index 2d980db18..5721e2882 100644
--- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -10,7 +10,7 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Drawing;
using Microsoft.Extensions.Logging;
using SkiaSharp;
-using SKSvg = SkiaSharp.Extended.Svg.SKSvg;
+using Svg.Skia;
namespace Jellyfin.Drawing.Skia;
@@ -23,6 +23,30 @@ public class SkiaEncoder : IImageEncoder
private readonly ILogger<SkiaEncoder> _logger;
private readonly IApplicationPaths _appPaths;
+ private static readonly SKImageFilter _imageFilter;
+
+#pragma warning disable CA1810
+ static SkiaEncoder()
+#pragma warning restore CA1810
+ {
+ var kernel = new[]
+ {
+ 0, -.1f, 0,
+ -.1f, 1.4f, -.1f,
+ 0, -.1f, 0,
+ };
+
+ var kernelSize = new SKSizeI(3, 3);
+ var kernelOffset = new SKPointI(1, 1);
+ _imageFilter = SKImageFilter.CreateMatrixConvolution(
+ kernelSize,
+ kernel,
+ 1f,
+ 0f,
+ kernelOffset,
+ SKShaderTileMode.Clamp,
+ true);
+ }
/// <summary>
/// Initializes a new instance of the <see cref="SkiaEncoder"/> class.
@@ -119,11 +143,17 @@ public class SkiaEncoder : IImageEncoder
var extension = Path.GetExtension(path.AsSpan());
if (extension.Equals(".svg", StringComparison.OrdinalIgnoreCase))
{
- var svg = new SKSvg();
+ using var svg = new SKSvg();
try
{
- svg.Load(path);
- return new ImageDimensions(Convert.ToInt32(svg.Picture.CullRect.Width), Convert.ToInt32(svg.Picture.CullRect.Height));
+ using var picture = svg.Load(path);
+ if (picture is null)
+ {
+ _logger.LogError("Unable to determine image dimensions for {FilePath}", path);
+ return default;
+ }
+
+ return new ImageDimensions(Convert.ToInt32(picture.CullRect.Width), Convert.ToInt32(picture.CullRect.Height));
}
catch (FormatException skiaColorException)
{
@@ -188,7 +218,7 @@ public class SkiaEncoder : IImageEncoder
return path;
}
- var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + Path.GetExtension(path));
+ var tempPath = Path.Combine(_appPaths.TempDirectory, string.Concat(Guid.NewGuid().ToString(), Path.GetExtension(path.AsSpan())));
var directory = Path.GetDirectoryName(tempPath) ?? throw new ResourceNotFoundException($"Provided path ({tempPath}) is not valid.");
Directory.CreateDirectory(directory);
File.Copy(path, tempPath, true);
@@ -200,20 +230,10 @@ public class SkiaEncoder : IImageEncoder
{
if (!orientation.HasValue)
{
- return SKEncodedOrigin.TopLeft;
+ return SKEncodedOrigin.Default;
}
- return orientation.Value switch
- {
- ImageOrientation.TopRight => SKEncodedOrigin.TopRight,
- ImageOrientation.RightTop => SKEncodedOrigin.RightTop,
- ImageOrientation.RightBottom => SKEncodedOrigin.RightBottom,
- ImageOrientation.LeftTop => SKEncodedOrigin.LeftTop,
- ImageOrientation.LeftBottom => SKEncodedOrigin.LeftBottom,
- ImageOrientation.BottomRight => SKEncodedOrigin.BottomRight,
- ImageOrientation.BottomLeft => SKEncodedOrigin.BottomLeft,
- _ => SKEncodedOrigin.TopLeft
- };
+ return (SKEncodedOrigin)orientation.Value;
}
/// <summary>
@@ -295,10 +315,7 @@ public class SkiaEncoder : IImageEncoder
private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
{
- var needsFlip = origin == SKEncodedOrigin.LeftBottom
- || origin == SKEncodedOrigin.LeftTop
- || origin == SKEncodedOrigin.RightBottom
- || origin == SKEncodedOrigin.RightTop;
+ var needsFlip = origin is SKEncodedOrigin.LeftBottom or SKEncodedOrigin.LeftTop or SKEncodedOrigin.RightBottom or SKEncodedOrigin.RightTop;
var rotated = needsFlip
? new SKBitmap(bitmap.Height, bitmap.Width)
: new SKBitmap(bitmap.Width, bitmap.Height);
@@ -363,25 +380,7 @@ public class SkiaEncoder : IImageEncoder
IsDither = isDither
};
- var kernel = new float[9]
- {
- 0, -.1f, 0,
- -.1f, 1.4f, -.1f,
- 0, -.1f, 0,
- };
-
- var kernelSize = new SKSizeI(3, 3);
- var kernelOffset = new SKPointI(1, 1);
-
- paint.ImageFilter = SKImageFilter.CreateMatrixConvolution(
- kernelSize,
- kernel,
- 1f,
- 0f,
- kernelOffset,
- SKShaderTileMode.Clamp,
- true);
-
+ paint.ImageFilter = _imageFilter;
canvas.DrawBitmap(
source,
SKRect.Create(0, 0, source.Width, source.Height),
@@ -432,7 +431,8 @@ public class SkiaEncoder : IImageEncoder
// scale image (the FromImage creates a copy)
var imageInfo = new SKImageInfo(width, height, bitmap.ColorType, bitmap.AlphaType, bitmap.ColorSpace);
- using var resizedBitmap = SKBitmap.FromImage(ResizeImage(bitmap, imageInfo));
+ using var resizedImage = ResizeImage(bitmap, imageInfo);
+ using var resizedBitmap = SKBitmap.FromImage(resizedImage);
// If all we're doing is resizing then we can stop now
if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
@@ -489,10 +489,8 @@ public class SkiaEncoder : IImageEncoder
Directory.CreateDirectory(directory);
using (var outputStream = new SKFileWStream(outputPath))
{
- using (var pixmap = new SKPixmap(new SKImageInfo(width, height), saveBitmap.GetPixels()))
- {
- pixmap.Encode(outputStream, skiaOutputFormat, quality);
- }
+ using var pixmap = new SKPixmap(new SKImageInfo(width, height), saveBitmap.GetPixels());
+ pixmap.Encode(outputStream, skiaOutputFormat, quality);
}
return outputPath;
@@ -526,6 +524,81 @@ public class SkiaEncoder : IImageEncoder
splashBuilder.GenerateSplash(posters, backdrops, outputPath);
}
+ /// <inheritdoc />
+ public int CreateTrickplayTile(ImageCollageOptions options, int quality, int imgWidth, int? imgHeight)
+ {
+ var paths = options.InputPaths;
+ var tileWidth = options.Width;
+ var tileHeight = options.Height;
+
+ if (paths.Count < 1)
+ {
+ throw new ArgumentException("InputPaths cannot be empty.");
+ }
+ else if (paths.Count > tileWidth * tileHeight)
+ {
+ throw new ArgumentException($"InputPaths contains more images than would fit on {tileWidth}x{tileHeight} grid.");
+ }
+
+ // If no height provided, use height of first image.
+ if (!imgHeight.HasValue)
+ {
+ using var firstImg = Decode(paths[0], false, null, out _);
+
+ if (firstImg is null)
+ {
+ throw new InvalidDataException("Could not decode image data.");
+ }
+
+ if (firstImg.Width != imgWidth)
+ {
+ throw new InvalidOperationException("Image width does not match provided width.");
+ }
+
+ imgHeight = firstImg.Height;
+ }
+
+ // Make horizontal strips using every provided image.
+ using var tileGrid = new SKBitmap(imgWidth * tileWidth, imgHeight.Value * tileHeight);
+ using var canvas = new SKCanvas(tileGrid);
+
+ var imgIndex = 0;
+ for (var y = 0; y < tileHeight; y++)
+ {
+ for (var x = 0; x < tileWidth; x++)
+ {
+ if (imgIndex >= paths.Count)
+ {
+ break;
+ }
+
+ using var img = Decode(paths[imgIndex++], false, null, out _);
+
+ if (img is null)
+ {
+ throw new InvalidDataException("Could not decode image data.");
+ }
+
+ if (img.Width != imgWidth)
+ {
+ throw new InvalidOperationException("Image width does not match provided width.");
+ }
+
+ if (img.Height != imgHeight)
+ {
+ throw new InvalidOperationException("Image height does not match first image height.");
+ }
+
+ canvas.DrawBitmap(img, x * imgWidth, y * imgHeight.Value);
+ }
+ }
+
+ using var outputStream = new SKFileWStream(options.OutputPath);
+ tileGrid.Encode(outputStream, SKEncodedImageFormat.Jpeg, quality);
+
+ return imgHeight.Value;
+ }
+
private void DrawIndicator(SKCanvas canvas, int imageWidth, int imageHeight, ImageProcessingOptions options)
{
try
diff --git a/src/Jellyfin.Drawing.Skia/SkiaHelper.cs b/src/Jellyfin.Drawing.Skia/SkiaHelper.cs
index 00d224da9..bd1b2b0da 100644
--- a/src/Jellyfin.Drawing.Skia/SkiaHelper.cs
+++ b/src/Jellyfin.Drawing.Skia/SkiaHelper.cs
@@ -19,7 +19,6 @@ public static class SkiaHelper
public static SKBitmap? GetNextValidImage(SkiaEncoder skiaEncoder, IReadOnlyList<string> paths, int currentIndex, out int newIndex)
{
var imagesTested = new Dictionary<int, int>();
- SKBitmap? bitmap = null;
while (imagesTested.Count < paths.Count)
{
@@ -28,7 +27,7 @@ public static class SkiaHelper
currentIndex = 0;
}
- bitmap = skiaEncoder.Decode(paths[currentIndex], false, null, out _);
+ SKBitmap? bitmap = skiaEncoder.Decode(paths[currentIndex], false, null, out _);
imagesTested[currentIndex] = 0;
@@ -36,11 +35,12 @@ public static class SkiaHelper
if (bitmap is not null)
{
- break;
+ newIndex = currentIndex;
+ return bitmap;
}
}
newIndex = currentIndex;
- return bitmap;
+ return null;
}
}
diff --git a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
index a8f80f7e2..4aff26c16 100644
--- a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
+++ b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
@@ -38,25 +38,25 @@ public partial class StripCollageBuilder
{
ArgumentNullException.ThrowIfNull(outputPath);
- var ext = Path.GetExtension(outputPath);
+ var ext = Path.GetExtension(outputPath.AsSpan());
- if (string.Equals(ext, ".jpg", StringComparison.OrdinalIgnoreCase)
- || string.Equals(ext, ".jpeg", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".jpg", StringComparison.OrdinalIgnoreCase)
+ || ext.Equals(".jpeg", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Jpeg;
}
- if (string.Equals(ext, ".webp", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".webp", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Webp;
}
- if (string.Equals(ext, ".gif", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".gif", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Gif;
}
- if (string.Equals(ext, ".bmp", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".bmp", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Bmp;
}
@@ -189,12 +189,12 @@ public partial class StripCollageBuilder
// Scale image. The FromBitmap creates a copy
var imageInfo = new SKImageInfo(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType, currentBitmap.ColorSpace);
- using var resizedBitmap = SKBitmap.FromImage(SkiaEncoder.ResizeImage(currentBitmap, imageInfo));
+ using var resizeImage = SkiaEncoder.ResizeImage(currentBitmap, imageInfo);
// draw this image into the strip at the next position
var xPos = x * cellWidth;
var yPos = y * cellHeight;
- canvas.DrawBitmap(resizedBitmap, xPos, yPos);
+ canvas.DrawImage(resizeImage, xPos, yPos);
}
}
diff --git a/src/Jellyfin.Drawing/ImageProcessor.cs b/src/Jellyfin.Drawing/ImageProcessor.cs
index 4e5d3b4d5..65a8f4e83 100644
--- a/src/Jellyfin.Drawing/ImageProcessor.cs
+++ b/src/Jellyfin.Drawing/ImageProcessor.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -109,24 +108,10 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation;
/// <inheritdoc />
- public async Task ProcessImage(ImageProcessingOptions options, Stream toStream)
- {
- var file = await ProcessImage(options).ConfigureAwait(false);
- using (var fileStream = AsyncFile.OpenRead(file.Path))
- {
- await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
- }
- }
-
- /// <inheritdoc />
public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
=> _imageEncoder.SupportedOutputFormats;
/// <inheritdoc />
- public bool SupportsTransparency(string path)
- => _transparentImageTypes.Contains(Path.GetExtension(path));
-
- /// <inheritdoc />
public async Task<(string Path, string? MimeType, DateTime DateModified)> ProcessImage(ImageProcessingOptions options)
{
ItemImageInfo originalImage = options.Image;
@@ -227,7 +212,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
}
}
- return (cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
+ return (cacheFilePath, outputFormat.GetMimeType(), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
}
catch (Exception ex)
{
@@ -265,17 +250,6 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
return ImageFormat.Jpg;
}
- private string GetMimeType(ImageFormat format, string path)
- => format switch
- {
- ImageFormat.Bmp => MimeTypes.GetMimeType("i.bmp"),
- ImageFormat.Gif => MimeTypes.GetMimeType("i.gif"),
- ImageFormat.Jpg => MimeTypes.GetMimeType("i.jpg"),
- ImageFormat.Png => MimeTypes.GetMimeType("i.png"),
- ImageFormat.Webp => MimeTypes.GetMimeType("i.webp"),
- _ => MimeTypes.GetMimeType(path)
- };
-
/// <summary>
/// Gets the cache file path based on a set of parameters.
/// </summary>
@@ -377,7 +351,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
filename.Append(",v=");
filename.Append(Version);
- return GetCachePath(ResizedImageCachePath, filename.ToString(), "." + format.ToString().ToLowerInvariant());
+ return GetCachePath(ResizedImageCachePath, filename.ToString(), format.GetExtension());
}
/// <inheritdoc />
@@ -437,8 +411,13 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
=> (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
/// <inheritdoc />
- public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
+ public string? GetImageCacheTag(BaseItem item, ChapterInfo chapter)
{
+ if (chapter.ImagePath is null)
+ {
+ return null;
+ }
+
return GetImageCacheTag(item, new ItemImageInfo
{
Path = chapter.ImagePath,
@@ -469,35 +448,6 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
return Task.FromResult((originalImagePath, dateModified));
}
- // TODO _mediaEncoder.ConvertImage is not implemented
- // if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat))
- // {
- // try
- // {
- // string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
- //
- // string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png";
- // var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
- //
- // var file = _fileSystem.GetFileInfo(outputPath);
- // if (!file.Exists)
- // {
- // await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false);
- // dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath);
- // }
- // else
- // {
- // dateModified = file.LastWriteTimeUtc;
- // }
- //
- // originalImagePath = outputPath;
- // }
- // catch (Exception ex)
- // {
- // _logger.LogError(ex, "Image conversion failed for {Path}", originalImagePath);
- // }
- // }
-
return Task.FromResult((originalImagePath, dateModified));
}
diff --git a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj
index e0963ac34..d7ef6f8e7 100644
--- a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj
+++ b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj
@@ -21,15 +21,4 @@
<Compile Include="..\..\SharedVersion.cs" />
</ItemGroup>
- <!-- Code analysers-->
- <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
- </PackageReference>
- <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
- </ItemGroup>
-
</Project>
diff --git a/src/Jellyfin.Drawing/NullImageEncoder.cs b/src/Jellyfin.Drawing/NullImageEncoder.cs
index 171128bed..1495661c1 100644
--- a/src/Jellyfin.Drawing/NullImageEncoder.cs
+++ b/src/Jellyfin.Drawing/NullImageEncoder.cs
@@ -50,6 +50,12 @@ public class NullImageEncoder : IImageEncoder
}
/// <inheritdoc />
+ public int CreateTrickplayTile(ImageCollageOptions options, int quality, int imgWidth, int? imgHeight)
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <inheritdoc />
public string GetImageBlurHash(int xComp, int yComp, string path)
{
throw new NotImplementedException();
diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj
index 4f80aa941..997df6dbe 100644
--- a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj
+++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj
@@ -27,20 +27,8 @@
<Compile Include="../../SharedVersion.cs" />
</ItemGroup>
-
<ItemGroup>
<PackageReference Include="Diacritics" />
</ItemGroup>
- <!-- Code Analyzers-->
- <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
- </PackageReference>
- <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
- </ItemGroup>
-
</Project>
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
index 321cfa502..17096c017 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs
@@ -59,7 +59,7 @@ namespace Jellyfin.Extensions.Json.Converters
var typedValueIndex = 0;
for (var i = 0; i < stringEntries.Length; i++)
{
- if (parsedValues[i] != null)
+ if (parsedValues[i] is not null)
{
typedValues.SetValue(parsedValues[i], typedValueIndex);
typedValueIndex++;
diff --git a/src/Jellyfin.Extensions/StreamExtensions.cs b/src/Jellyfin.Extensions/StreamExtensions.cs
index 9751d9d42..182996852 100644
--- a/src/Jellyfin.Extensions/StreamExtensions.cs
+++ b/src/Jellyfin.Extensions/StreamExtensions.cs
@@ -26,10 +26,8 @@ namespace Jellyfin.Extensions
/// <returns>All lines in the stream.</returns>
public static string[] ReadAllLines(this Stream stream, Encoding encoding)
{
- using (StreamReader reader = new StreamReader(stream, encoding))
- {
- return ReadAllLines(reader).ToArray();
- }
+ using StreamReader reader = new StreamReader(stream, encoding);
+ return ReadAllLines(reader).ToArray();
}
/// <summary>
@@ -40,7 +38,7 @@ namespace Jellyfin.Extensions
public static IEnumerable<string> ReadAllLines(this TextReader reader)
{
string? line;
- while ((line = reader.ReadLine()) != null)
+ while ((line = reader.ReadLine()) is not null)
{
yield return line;
}
@@ -54,7 +52,7 @@ namespace Jellyfin.Extensions
public static async IAsyncEnumerable<string> ReadAllLinesAsync(this TextReader reader)
{
string? line;
- while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null)
+ while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) is not null)
{
yield return line;
}
diff --git a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj
index 3f4f55ee4..76dde1cf6 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj
+++ b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj
@@ -5,17 +5,6 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
- <!-- Code Analyzers-->
- <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
- </PackageReference>
- <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
- </ItemGroup>
-
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />
<ProjectReference Include="../../MediaBrowser.Controller/MediaBrowser.Controller.csproj" />
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs
index febe9516a..479e6ffdc 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs
+++ b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs
@@ -68,51 +68,54 @@ public static class FfProbeKeyframeExtractor
double streamDuration = 0;
double formatDuration = 0;
- while (!reader.EndOfStream)
+ using (reader)
{
- var line = reader.ReadLine().AsSpan();
- if (line.IsEmpty)
+ while (!reader.EndOfStream)
{
- continue;
- }
+ var line = reader.ReadLine().AsSpan();
+ if (line.IsEmpty)
+ {
+ continue;
+ }
- var firstComma = line.IndexOf(',');
- var lineType = line[..firstComma];
- var rest = line[(firstComma + 1)..];
- if (lineType.Equals("packet", StringComparison.OrdinalIgnoreCase))
- {
- // Split time and flags from the packet line. Example line: packet,7169.079000,K_
- var secondComma = rest.IndexOf(',');
- var ptsTime = rest[..secondComma];
- var flags = rest[(secondComma + 1)..];
- if (flags.StartsWith("K_"))
+ var firstComma = line.IndexOf(',');
+ var lineType = line[..firstComma];
+ var rest = line[(firstComma + 1)..];
+ if (lineType.Equals("packet", StringComparison.OrdinalIgnoreCase))
{
- if (double.TryParse(ptsTime, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var keyframe))
+ // Split time and flags from the packet line. Example line: packet,7169.079000,K_
+ var secondComma = rest.IndexOf(',');
+ var ptsTime = rest[..secondComma];
+ var flags = rest[(secondComma + 1)..];
+ if (flags.StartsWith("K_"))
{
- // Have to manually convert to ticks to avoid rounding errors as TimeSpan is only precise down to 1 ms when converting double.
- keyframes.Add(Convert.ToInt64(keyframe * TimeSpan.TicksPerSecond));
+ if (double.TryParse(ptsTime, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var keyframe))
+ {
+ // Have to manually convert to ticks to avoid rounding errors as TimeSpan is only precise down to 1 ms when converting double.
+ keyframes.Add(Convert.ToInt64(keyframe * TimeSpan.TicksPerSecond));
+ }
}
}
- }
- else if (lineType.Equals("stream", StringComparison.OrdinalIgnoreCase))
- {
- if (double.TryParse(rest, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var streamDurationResult))
+ else if (lineType.Equals("stream", StringComparison.OrdinalIgnoreCase))
{
- streamDuration = streamDurationResult;
+ if (double.TryParse(rest, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var streamDurationResult))
+ {
+ streamDuration = streamDurationResult;
+ }
}
- }
- else if (lineType.Equals("format", StringComparison.OrdinalIgnoreCase))
- {
- if (double.TryParse(rest, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var formatDurationResult))
+ else if (lineType.Equals("format", StringComparison.OrdinalIgnoreCase))
{
- formatDuration = formatDurationResult;
+ if (double.TryParse(rest, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var formatDurationResult))
+ {
+ formatDuration = formatDurationResult;
+ }
}
}
- }
- // Prefer the stream duration as it should be more accurate
- var duration = streamDuration > 0 ? streamDuration : formatDuration;
+ // Prefer the stream duration as it should be more accurate
+ var duration = streamDuration > 0 ? streamDuration : formatDuration;
- return new KeyframeData(TimeSpan.FromSeconds(duration).Ticks, keyframes);
+ return new KeyframeData(TimeSpan.FromSeconds(duration).Ticks, keyframes);
+ }
}
}
diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj
index 71572bcf6..0d91a447b 100644
--- a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj
+++ b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj
@@ -9,17 +9,6 @@
<PackageReference Include="NEbml" />
</ItemGroup>
- <!-- Code Analyzers-->
- <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
- </PackageReference>
- <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
- </ItemGroup>
-
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>