aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Providers
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-07-21 07:14:47 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-07-21 07:14:47 +0200
commitb99703301f43927f632bbc49d040a32824d43cb3 (patch)
treed57fed2bd6b658ddf6b1ac3ed4d2a33945f8688a /MediaBrowser.Providers
parent21801e8ba138af71c4c58489ea33534adf7426c5 (diff)
parentbdf263d8677ee87078c680a355a91a8048b8308b (diff)
Merge remote-tracking branch 'upstream/master' into security-path-traversal-fixes
# Conflicts: # Jellyfin.Api/Controllers/HlsSegmentController.cs # Jellyfin.Api/Controllers/PluginsController.cs
Diffstat (limited to 'MediaBrowser.Providers')
-rw-r--r--MediaBrowser.Providers/Books/OpenPackagingFormat/OpfReader.cs26
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs11
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/MediaInfo/ProbeProvider.cs54
-rwxr-xr-xMediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs30
5 files changed, 111 insertions, 11 deletions
diff --git a/MediaBrowser.Providers/Books/OpenPackagingFormat/OpfReader.cs b/MediaBrowser.Providers/Books/OpenPackagingFormat/OpfReader.cs
index b4e3fd3089..6266413dfc 100644
--- a/MediaBrowser.Providers/Books/OpenPackagingFormat/OpfReader.cs
+++ b/MediaBrowser.Providers/Books/OpenPackagingFormat/OpfReader.cs
@@ -2,6 +2,7 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using Jellyfin.Data.Enums;
@@ -17,7 +18,7 @@ namespace MediaBrowser.Providers.Books.OpenPackagingFormat
/// Methods used to pull metadata and other information from Open Packaging Format in XML objects.
/// </summary>
/// <typeparam name="TCategoryName">The type of category.</typeparam>
- public class OpfReader<TCategoryName>
+ public partial class OpfReader<TCategoryName>
{
private const string DcNamespace = @"http://purl.org/dc/elements/1.1/";
private const string OpfNamespace = @"http://www.idpf.org/2007/opf";
@@ -42,6 +43,9 @@ namespace MediaBrowser.Providers.Books.OpenPackagingFormat
_namespaceManager.AddNamespace("opf", OpfNamespace);
}
+ [GeneratedRegex(@"(?<=\p{L})\.(?!\s|$)")]
+ private static partial Regex InitialsRegex();
+
/// <summary>
/// Checks for the existence of a cover image.
/// </summary>
@@ -229,11 +233,23 @@ namespace MediaBrowser.Providers.Books.OpenPackagingFormat
{
foreach (XmlElement creator in resultElement)
{
- var creatorName = creator.InnerText;
var role = creator.GetAttribute("opf:role");
- var person = new PersonInfo { Name = creatorName, Type = GetRole(role) };
-
- book.AddPerson(person);
+ var normalizedCreators = creator.InnerText
+ .Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
+ .Select(fullName =>
+ {
+ if (fullName.Split(',', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) is [var lastName, var firstName])
+ {
+ fullName = $"{firstName} {lastName}";
+ }
+
+ return InitialsRegex().Replace(fullName, ". ");
+ });
+
+ foreach (var fullName in normalizedCreators)
+ {
+ book.AddPerson(new PersonInfo { Name = fullName, Type = GetRole(role) });
+ }
}
}
}
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 118ccf8679..a438a94c40 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -680,11 +680,18 @@ namespace MediaBrowser.Providers.Manager
return providers;
}
- protected virtual IEnumerable<IImageProvider> GetNonLocalImageProviders(BaseItem item, IEnumerable<IImageProvider> allImageProviders, ImageRefreshOptions options)
+ protected virtual IEnumerable<IImageProvider> GetNonLocalImageProviders(BaseItem item, IEnumerable<IImageProvider> allImageProviders, MetadataRefreshOptions options)
{
// Get providers to refresh
var providers = allImageProviders.Where(i => i is not ILocalImageProvider);
+ // When identifying, run the provider the user picked first so the correct image is used.
+ if (!string.IsNullOrEmpty(options.SearchResult?.SearchProviderName))
+ {
+ providers = providers
+ .OrderBy(i => string.Equals(i.Name, options.SearchResult.SearchProviderName, StringComparison.OrdinalIgnoreCase) ? 0 : 1);
+ }
+
var dateLastImageRefresh = item.DateLastRefreshed;
// Run all if either of these flags are true
@@ -1116,7 +1123,7 @@ namespace MediaBrowser.Providers.Manager
{
if (replaceData || !target.RunTimeTicks.HasValue)
{
- if (target is not Audio && target is not Video)
+ if (target is not Audio && target is not Video && target is not Book)
{
target.RunTimeTicks = source.RunTimeTicks;
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index df51dd8421..2b0f480b1c 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -21,6 +21,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Newtonsoft.Json" />
+ <PackageReference Include="PDFtoImage" />
<PackageReference Include="PlaylistsNET" />
<PackageReference Include="SharpCompress" />
<PackageReference Include="z440.atl.core" />
diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
index 789df8f061..221c6bff5e 100644
--- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
@@ -24,6 +24,8 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging;
+using PDFtoImage;
+using SharpCompress.Archives;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -37,6 +39,7 @@ namespace MediaBrowser.Providers.MediaInfo
ICustomMetadataProvider<Video>,
ICustomMetadataProvider<Audio>,
ICustomMetadataProvider<AudioBook>,
+ ICustomMetadataProvider<Book>,
IHasOrder,
IForcedProvider,
IPreRefreshProvider,
@@ -214,6 +217,57 @@ namespace MediaBrowser.Providers.MediaInfo
return FetchAudioInfo(item, options, cancellationToken);
}
+ /// <inheritdoc />
+ public Task<ItemUpdateType> FetchAsync(Book item, MetadataRefreshOptions options, CancellationToken cancellationToken)
+ {
+ if (item.IsVirtualItem || !item.IsFileProtocol)
+ {
+ return _cachedTask;
+ }
+
+ long pageCount;
+ switch (Path.GetExtension(item.Path).ToLowerInvariant())
+ {
+ case ".cb7":
+ case ".cbr":
+ case ".cbt":
+ case ".cbz":
+ using (var stream = File.OpenRead(item.Path))
+ using (var archive = ArchiveFactory.OpenArchive(stream))
+ {
+ pageCount = archive.Entries.Count(e => !e.IsDirectory);
+ }
+
+ break;
+
+#pragma warning disable CA1416
+ case ".pdf":
+ using (var stream = File.OpenRead(item.Path))
+ {
+ pageCount = Conversion.GetPageCount(stream);
+ }
+
+ break;
+#pragma warning restore CA1416
+
+ case ".epub":
+ // TODO process CFI and store as a string when multiple progress types are supported
+ // current progress value is percentage stored as a proportion of one second worth of ticks
+ item.RunTimeTicks = TimeSpan.TicksPerSecond;
+
+ return Task.FromResult(ItemUpdateType.MetadataImport);
+
+ default:
+ return _cachedTask;
+ }
+
+ // TODO use page count without modification when multiple progress types are supported
+ // book players report page count and the web client multiplies that value by 10000 to convert the expected milliseconds into ticks
+ item.RunTimeTicks = pageCount * 10000;
+
+ return Task.FromResult(ItemUpdateType.MetadataImport);
+ }
+
/// <summary>
/// Fetches video information for an item.
/// </summary>
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
index b2f9d13e73..9bb15ca479 100755
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
@@ -256,11 +256,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
series.Overview = seriesResult.Overview;
+ var studios = Enumerable.Empty<string>();
+
if (seriesResult.Networks is not null)
{
- series.Studios = seriesResult.Networks.Select(i => i.Name).ToArray();
+ studios = studios.Concat(seriesResult.Networks.Select(i => i.Name).OfType<string>());
+ }
+
+ if (seriesResult.ProductionCompanies is not null)
+ {
+ studios = studios.Concat(seriesResult.ProductionCompanies.Select(i => i.Name).OfType<string>());
}
+ series.SetStudios(studios);
+
if (seriesResult.Genres is not null)
{
series.Genres = seriesResult.Genres.Select(i => i.Name).ToArray();
@@ -320,13 +329,26 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
if (seriesResult.Videos?.Results is not null)
{
- foreach (var video in seriesResult.Videos.Results)
+ var trailers = new List<MediaUrl>();
+
+ var sortedVideos = seriesResult.Videos.Results
+ .OrderByDescending(video => string.Equals(video.Type, "trailer", StringComparison.OrdinalIgnoreCase));
+
+ foreach (var video in sortedVideos)
{
- if (TmdbUtils.IsTrailerType(video))
+ if (!TmdbUtils.IsTrailerType(video))
{
- series.AddTrailerUrl("https://www.youtube.com/watch?v=" + video.Key);
+ continue;
}
+
+ trailers.Add(new MediaUrl
+ {
+ Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", video.Key),
+ Name = video.Name
+ });
}
+
+ series.RemoteTrailers = trailers;
}
if (!string.IsNullOrEmpty(seriesResult.OriginalLanguage))