Merge branch 'jellyfin:master' into BDMV-pgs

This commit is contained in:
Oggeb1
2026-08-29 14:02:22 +02:00
committed by GitHub
112 changed files with 8614 additions and 861 deletions
@@ -173,10 +173,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
name;
var validName = normalizeName ? GetItemByNameFolderName(name) : name;
return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.ArtistsPath, validName);
}
@@ -80,10 +80,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
name;
var validName = normalizeName ? GetItemByNameFolderName(name) : name;
return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.MusicGenrePath, validName);
}
@@ -48,6 +48,10 @@ namespace MediaBrowser.Controller.Entities
public const string ThemeSongFileName = "theme";
// Well below the 255 byte limit of the common Linux filesystems and the 255 character limit
// of Windows, so the files inside the folder still fit within MAX_PATH.
private const int MaxItemByNameFolderNameBytes = 128;
/// <summary>
/// The supported image extensions.
/// </summary>
@@ -941,6 +945,43 @@ namespace MediaBrowser.Controller.Entities
return GetSortName(Name, EnableAlphaNumericSorting, ConfigurationManager.Configuration);
}
/// <summary>
/// Turns an item-by-name entity's name into a folder name every supported filesystem accepts.
/// </summary>
/// <param name="name">The entity's name.</param>
/// <returns>The folder name.</returns>
public static string GetItemByNameFolderName(string name)
{
// Trim the period at the end because windows will have a hard time with that
var validName = FileSystem.GetValidFilename(name).Trim().TrimEnd('.');
// Most Linux filesystems cap a path component at 255 bytes, so a name past that cannot be
// turned into a folder at all - and an entity with no folder can never be created, which
// leaves the credit behind it stuck: not refreshable, not deletable, retried on every scan.
// Only broken provider data gets this long, but it still has to resolve to something, so
// keep a readable prefix and let a hash of the whole name tell two of them apart.
if (Encoding.UTF8.GetByteCount(validName) <= MaxItemByNameFolderNameBytes)
{
return validName;
}
var suffix = "-" + validName.GetMD5().ToString("N", CultureInfo.InvariantCulture);
var budget = MaxItemByNameFolderNameBytes - suffix.Length;
var length = Math.Min(validName.Length, budget);
while (length > 0 && Encoding.UTF8.GetByteCount(validName.AsSpan(0, length)) > budget)
{
length--;
}
// Never cut a surrogate pair in half, the lone half is not a valid file name character.
if (length > 0 && char.IsHighSurrogate(validName[length - 1]))
{
length--;
}
return string.Concat(validName.AsSpan(0, length).TrimEnd().TrimEnd('.'), suffix);
}
/// <summary>
/// Cleans a raw name into its sortable form by applying the configured sort rules.
/// </summary>
+1 -4
View File
@@ -83,10 +83,7 @@ namespace MediaBrowser.Controller.Entities
public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
name;
var validName = normalizeName ? GetItemByNameFolderName(name) : name;
return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.GenrePath, validName);
}
+1 -4
View File
@@ -98,10 +98,7 @@ namespace MediaBrowser.Controller.Entities
public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validFilename = normalizeName ?
FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
name;
var validFilename = normalizeName ? GetItemByNameFolderName(name) : name;
string subFolderPrefix = null;
+1 -4
View File
@@ -78,10 +78,7 @@ namespace MediaBrowser.Controller.Entities
public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
name;
var validName = normalizeName ? GetItemByNameFolderName(name) : name;
return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.StudioPath, validName);
}
+16 -7
View File
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
@@ -89,15 +90,14 @@ namespace MediaBrowser.Controller.Entities.TV
if (!string.IsNullOrEmpty(groupingKey))
{
return AppendPreferredLanguage(groupingKey);
return AddLibrariesToPresentationUniqueKey(groupingKey);
}
}
return base.CreatePresentationUniqueKey();
}
// The owning libraries are deliberately NOT part of the key.
private string AppendPreferredLanguage(string key)
private string AddLibrariesToPresentationUniqueKey(string key)
{
var lang = GetPreferredMetadataLanguage();
if (!string.IsNullOrEmpty(lang))
@@ -105,7 +105,17 @@ namespace MediaBrowser.Controller.Entities.TV
key += "-" + lang;
}
return key;
var folders = LibraryManager.GetCollectionFolders(this)
.Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
.Order(StringComparer.Ordinal)
.ToArray();
if (folders.Length == 0)
{
return key;
}
return key + "-" + string.Join('-', folders);
}
private string GetNameBasedGroupingKey()
@@ -125,20 +135,19 @@ namespace MediaBrowser.Controller.Entities.TV
{
var seriesKey = GetUniqueSeriesKey(this);
var result = LibraryManager.GetCount(new InternalItemsQuery(user)
var result = LibraryManager.GetItemIds(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { BaseItemKind.Season },
IsVirtualItem = false,
Limit = 0,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
}
});
return result;
return result.Count;
}
public override int GetRecursiveChildCount(User user)
+1 -4
View File
@@ -85,10 +85,7 @@ namespace MediaBrowser.Controller.Entities
public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
name;
var validName = normalizeName ? GetItemByNameFolderName(name) : name;
return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.YearPath, validName);
}
@@ -605,6 +605,12 @@ namespace MediaBrowser.Controller.Library
/// <returns>List&lt;System.String&gt;.</returns>
IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query);
/// <summary>
/// Deletes every credit that no item maps to any more.
/// </summary>
/// <returns>The number of credits that were deleted.</returns>
int DeleteOrphanedCredits();
/// <summary>
/// Gets the distinct people names per item for multiple items.
/// </summary>
@@ -1318,7 +1318,7 @@ namespace MediaBrowser.Controller.MediaEncoding
arg.Append(canvasArgs);
}
arg.Append(" -i file:\"").Append(subtitlePath).Append('\"');
arg.Append(" -i file:\"").Append(subtitlePath.EscapeProcessArgument()).Append('\"');
}
if (state.AudioStream is not null && state.AudioStream.IsExternal)
@@ -1330,7 +1330,7 @@ namespace MediaBrowser.Controller.MediaEncoding
arg.Append(' ').Append(seekAudioParam);
}
arg.Append(" -i \"").Append(state.AudioStream.Path).Append('"');
arg.Append(" -i \"").Append(state.AudioStream.Path.EscapeProcessArgument()).Append('"');
}
// Disable auto inserted SW scaler for HW decoders in case of changed resolution.
@@ -33,6 +33,12 @@ public interface IPeopleRepository
/// <returns>The list of people names matching the filter.</returns>
IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery filter);
/// <summary>
/// Deletes every credit that no item maps to any more.
/// </summary>
/// <returns>The number of credits that were deleted.</returns>
int DeleteOrphanedCredits();
/// <summary>
/// Gets the distinct people names per item for multiple items efficiently by querying from the mapping table.
/// </summary>
@@ -16,11 +16,6 @@ namespace MediaBrowser.Controller.Providers
private List<(string Url, ImageType Type)> _remoteImages;
private List<PersonInfo> _people;
public MetadataResult()
{
ResultLanguage = "en";
}
public List<LocalImageInfo> Images
{
get => _images ??= [];
@@ -43,6 +38,9 @@ namespace MediaBrowser.Controller.Providers
public T Item { get; set; }
/// <summary>
/// Gets or sets the language the fetched metadata is in.
/// </summary>
public string ResultLanguage { get; set; }
public string Provider { get; set; }