Compare commits

..

10 Commits

Author SHA1 Message Date
renovate[bot]
1176c2d329 Update dependency SharpFuzz to 2.3.0 2026-06-16 11:32:11 +00:00
Rohith
e2433e2c79 Translated using Weblate (Kannada)
Some checks are pending
CodeQL / Analyze (csharp) (push) Waiting to run
Format / format-check (push) Waiting to run
Tests / run-tests (macos-latest) (push) Waiting to run
Tests / run-tests (ubuntu-latest) (push) Waiting to run
Tests / run-tests (windows-latest) (push) Waiting to run
OpenAPI Publish / OpenAPI - Publish Artifact (push) Waiting to run
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Blocked by required conditions
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Blocked by required conditions
Project Automation / Project board (push) Waiting to run
Merge Conflict Labeler / main (push) Waiting to run
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kn/
2026-06-15 20:31:51 +00:00
Bond-009
e4edce9a70 Merge pull request #17074 from jellyfin/renovate/sharpcompress-0.x
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Format / format-check (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / main (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Update dependency SharpCompress to 0.49.1
2026-06-15 20:56:39 +02:00
Bond_009
ac92da233b await instead of returning Task 2026-06-15 20:41:30 +02:00
Bond_009
f6bb086415 fix build errors 2026-06-15 18:52:20 +02:00
renovate[bot]
9375f31bd3 Update dependency SharpCompress to 0.49.1 2026-06-15 16:28:58 +00:00
Bond-009
a0862a4cb5 Merge pull request #17109 from jellyfin/renovate/serilog.settings.configuration-10.x
Update dependency Serilog.Settings.Configuration to 10.0.1
2026-06-15 18:23:06 +02:00
Bond-009
2d8ab1e2ec Merge pull request #17089 from Bond-009/sharpcompress
Replace usage of SharpCompress
2026-06-15 18:18:49 +02:00
renovate[bot]
8d0003533e Update dependency Serilog.Settings.Configuration to 10.0.1 2026-06-15 08:45:54 +00:00
Bond_009
d8f8dbabcb Replace usage of SharpCompress
ComicImageProvider is the last user of SharpCompress after this PR
2026-06-13 22:19:30 +02:00
6 changed files with 80 additions and 53 deletions

View File

@@ -61,20 +61,20 @@
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageVersion Include="Serilog.Expressions" Version="5.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="10.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="10.0.1" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Serilog.Sinks.Graylog" Version="3.1.1" />
<PackageVersion Include="SerilogAnalyzer" Version="0.15.0" />
<PackageVersion Include="SharpCompress" Version="0.38.0" />
<PackageVersion Include="SharpFuzz" Version="2.2.0" />
<PackageVersion Include="SharpCompress" Version="0.49.1" />
<PackageVersion Include="SharpFuzz" Version="2.3.0" />
<PackageVersion Include="SkiaSharp" Version="3.119.4" />
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="3.119.4" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="Svg.Skia" Version="5.1.1" />
<PackageVersion Include="Svg.Skia" Version="3.7.0" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="10.2.1" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.2.1" />
<PackageVersion Include="System.Text.Json" Version="10.0.9" />

View File

@@ -80,7 +80,7 @@
"NotificationOptionInstallationFailed": "ಸ್ಥಾಪನ ವೈಫಲ್ಯ",
"NotificationOptionNewLibraryContent": "ಹೊಸ ವಿಷಯವನ್ನು ಒಳಗೊಂಡಿದೆ",
"NotificationOptionPluginError": "ಪ್ಲಗಿನ್ ವೈಫಲ್ಯ",
"NotificationOptionPluginInstalled": "ಪ್ಲಗಿನ್ ವೈಫಲ್ಯ",
"NotificationOptionPluginInstalled": "ಪ್ಲಗಿನ್ ಸ್ಥಾಪಿಸಲಾಗಿದೆ",
"NotificationOptionPluginUpdateInstalled": "ಪ್ಲಗಿನ್ ನವೀಕರಣವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ",
"NotificationOptionServerRestartRequired": "ಸರ್ವರ್ ಮರುಪ್ರಾರಂಭದ ಅಗತ್ಯವಿದೆ",
"NotificationOptionTaskFailed": "ನಿಗದಿತ ಕಾರ್ಯ ವೈಫಲ್ಯ",

View File

@@ -1,6 +1,7 @@
using System;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.Json;
using System.Threading;
@@ -12,7 +13,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Books.ComicBookInfo.Models;
using Microsoft.Extensions.Logging;
using SharpCompress.Archives.Zip;
namespace MediaBrowser.Providers.Books.ComicBookInfo;
@@ -48,35 +48,27 @@ public class ComicBookInfoProvider : IComicProvider
try
{
Stream stream = File.OpenRead(path);
// not yet async: https://github.com/adamhathcock/sharpcompress/pull/565
Stream stream = AsyncFile.OpenRead(path);
await using (stream.ConfigureAwait(false))
using (var archive = ZipArchive.Open(stream))
{
if (!archive.IsComplete)
var archive = await ZipArchive.CreateAsync(stream, ZipArchiveMode.Read, false, null, cancellationToken).ConfigureAwait(false);
await using (archive.ConfigureAwait(false))
{
_logger.LogError("incomplete comic archive: {Path}", info.Path);
return new MetadataResult<Book> { HasMetadata = false };
if (archive.Comment is null)
{
_logger.LogInformation("missing ComicBookInfo in archive comment: {Path}", info.Path);
return new MetadataResult<Book> { HasMetadata = false };
}
var comicBookMetadata = JsonSerializer.Deserialize<ComicBookInfoFormat>(archive.Comment, JsonDefaults.Options);
if (comicBookMetadata is null)
{
_logger.LogError("ComicBookInfo deserialization failure: {Path}", info.Path);
return new MetadataResult<Book> { HasMetadata = false };
}
return SaveMetadata(comicBookMetadata);
}
var volume = archive.Volumes.First();
if (volume.Comment is null)
{
_logger.LogInformation("missing ComicBookInfo in archive comment: {Path}", info.Path);
return new MetadataResult<Book> { HasMetadata = false };
}
var comicBookMetadata = JsonSerializer.Deserialize<ComicBookInfoFormat>(volume.Comment, JsonDefaults.Options);
if (comicBookMetadata is null)
{
_logger.LogError("ComicBookInfo deserialization failure: {Path}", info.Path);
return new MetadataResult<Book> { HasMetadata = false };
}
return SaveMetadata(comicBookMetadata);
}
}
catch (Exception ex)

View File

@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
using SharpCompress.Archives;
@@ -38,16 +39,16 @@ public class ComicImageProvider : IDynamicImageProvider
public string Name => "Comic Book Archive Cover Extractor";
/// <inheritdoc />
public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
public async Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
{
var extension = Path.GetExtension(item.Path);
if (_comicBookExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
return LoadCover(item);
return await LoadCoverAsync(item, cancellationToken).ConfigureAwait(false);
}
return Task.FromResult(new DynamicImageResponse { HasImage = false });
return new DynamicImageResponse { HasImage = false };
}
/// <inheritdoc />
@@ -67,7 +68,8 @@ public class ComicImageProvider : IDynamicImageProvider
/// with no image if nothing is found.
/// </summary>
/// <param name="item">Item to check for covers.</param>
private async Task<DynamicImageResponse> LoadCover(BaseItem item)
/// <param name="cancellationToken">The cancellation token.</param>
private async Task<DynamicImageResponse> LoadCoverAsync(BaseItem item, CancellationToken cancellationToken)
{
var memoryStream = new MemoryStream();
@@ -75,14 +77,22 @@ public class ComicImageProvider : IDynamicImageProvider
{
ImageFormat imageFormat;
using (Stream stream = File.OpenRead(item.Path))
using (var archive = ArchiveFactory.Open(stream))
using (Stream stream = AsyncFile.OpenRead(item.Path))
{
// throw exception to log results if no cover is found
(var cover, imageFormat) = FindCoverEntryInArchive(archive) ?? throw new InvalidOperationException("no supported cover found");
var archive = await ArchiveFactory.OpenAsyncArchive(stream, cancellationToken: cancellationToken).ConfigureAwait(false);
await using (archive.ConfigureAwait(false))
{
// throw exception to log results if no cover is found
(var cover, imageFormat) = await FindCoverEntryInArchiveAsync(archive).ConfigureAwait(false)
?? throw new InvalidOperationException("no supported cover found");
// copy the cover to memory stream
await cover.OpenEntryStream().CopyToAsync(memoryStream).ConfigureAwait(false);
// copy the cover to memory stream
var coverStream = await cover.OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
await using (coverStream.ConfigureAwait(false))
{
await coverStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
}
}
}
// reset stream position after copying
@@ -102,7 +112,7 @@ public class ComicImageProvider : IDynamicImageProvider
/// </summary>
/// <param name="archive">The archive to search.</param>
/// <returns>The search result.</returns>
private (IArchiveEntry CoverEntry, ImageFormat ImageFormat)? FindCoverEntryInArchive(IArchive archive)
private async ValueTask<(IArchiveEntry CoverEntry, ImageFormat ImageFormat)?> FindCoverEntryInArchiveAsync(IAsyncArchive archive)
{
IArchiveEntry? cover;
@@ -110,7 +120,7 @@ public class ComicImageProvider : IDynamicImageProvider
// in many cases the cover will simply be the first image in the archive
foreach (var extension in _coverExtensions)
{
cover = archive.Entries.FirstOrDefault(e => e.Key == "cover" + extension);
cover = await archive.EntriesAsync.FirstOrDefaultAsync(e => e.Key == "cover" + extension).ConfigureAwait(false);
if (cover is not null)
{
@@ -120,7 +130,9 @@ public class ComicImageProvider : IDynamicImageProvider
}
}
cover = archive.Entries.OrderBy(x => x.Key).FirstOrDefault(x => _coverExtensions.Contains(Path.GetExtension(x.Key), StringComparison.OrdinalIgnoreCase));
cover = await archive.EntriesAsync.OrderBy(x => x.Key)
.FirstOrDefaultAsync(x => _coverExtensions.Contains(Path.GetExtension(x.Key), StringComparison.OrdinalIgnoreCase))
.ConfigureAwait(false);
if (cover is not null)
{

View File

@@ -7,7 +7,6 @@ using System.Xml.XPath;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using SharpCompress;
namespace MediaBrowser.Providers.Books.ComicInfo;
@@ -41,7 +40,13 @@ public static class ComicInfoReader
hasFoundMetadata |= ReadStringInto(xml, "ComicInfo/Summary", summary => book.Overview = summary);
hasFoundMetadata |= ReadIntInto(xml, "ComicInfo/Year", year => book.ProductionYear = year);
hasFoundMetadata |= ReadThreePartDateInto(xml, "ComicInfo/Year", "ComicInfo/Month", "ComicInfo/Day", dateTime => book.PremiereDate = dateTime);
hasFoundMetadata |= ReadCommaSeparatedStringsInto(xml, "ComicInfo/Genre", genres => genres.ForEach(genre => book.AddGenre(genre)));
hasFoundMetadata |= ReadCommaSeparatedStringsInto(xml, "ComicInfo/Genre", genres =>
{
foreach (var genre in genres)
{
book.AddGenre(genre);
}
});
hasFoundMetadata |= ReadStringInto(xml, "ComicInfo/Publisher", publisher => book.SetStudios([publisher]));
hasFoundMetadata |= ReadStringInto(xml, "ComicInfo/AlternateSeries", title =>
@@ -71,32 +76,50 @@ public static class ComicInfoReader
{
ReadCommaSeparatedStringsInto(xml, "ComicInfo/Writer", authors =>
{
authors.ForEach(p => metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Author }));
foreach (var p in authors)
{
metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Author });
}
});
ReadCommaSeparatedStringsInto(xml, "ComicInfo/Penciller", pencillers =>
{
pencillers.ForEach(p => metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Penciller }));
foreach (var p in pencillers)
{
metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Penciller });
}
});
ReadCommaSeparatedStringsInto(xml, "ComicInfo/Inker", inkers =>
{
inkers.ForEach(p => metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Inker }));
foreach (var p in inkers)
{
metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Inker });
}
});
ReadCommaSeparatedStringsInto(xml, "ComicInfo/Letterer", letterers =>
{
letterers.ForEach(p => metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Letterer }));
foreach (var p in letterers)
{
metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Letterer });
}
});
ReadCommaSeparatedStringsInto(xml, "ComicInfo/CoverArtist", artists =>
{
artists.ForEach(p => metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.CoverArtist }));
foreach (var p in artists)
{
metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.CoverArtist });
}
});
ReadCommaSeparatedStringsInto(xml, "ComicInfo/Colourist", colorists =>
{
colorists.ForEach(p => metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Colorist }));
foreach (var p in colorists)
{
metadataResult.AddPerson(new PersonInfo { Name = p, Type = PersonKind.Colorist });
}
});
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;