Compare commits

..

10 Commits

Author SHA1 Message Date
gnattu
a78c3385c9 Don't list every item just to get count
This emby era logic now becomes a serious performance problem on very huge folders. Use the proper item count query for most common cases.
2025-12-02 00:34:22 +08:00
MBR-0001
daca285568 Revert "Localization/iso6392.txt: change pob and pop" (#15555) 2025-11-23 19:20:29 +01:00
theguymadmax
fbb9a0b2c7 Fix ResolveLinkTarget crashing on exFAT drives (#15568) 2025-11-21 21:14:39 -07:00
Ziyuan Qu
29b3aa8543 Add hidden file check in bdInfo (#15582) 2025-11-21 21:14:30 -07:00
theguymadmax
94f3725208 Fix isMovie filter logic (#15594) 2025-11-21 21:14:03 -07:00
theguymadmax
0ee81e87be Fix locked fields on not saving (#15564) 2025-11-19 17:02:53 +01:00
theguymadmax
c491a918c2 Save item to database before providers run to prevent FK constraint errors (#15563) 2025-11-19 17:01:13 +01:00
gnattu
1e7e46cb82 Prevent copying HDR streams when only SDR is supported (#15556) 2025-11-18 18:37:35 -07:00
theguymadmax
5ae444d96d Fix NullReferenceException in filesystem path comparison (#15548) 2025-11-18 18:37:09 -07:00
gnattu
ee7ad83427 Restrict first video frame probing to file protocol (#15557) 2025-11-18 18:36:59 -07:00
9 changed files with 85 additions and 43 deletions

View File

@@ -497,8 +497,17 @@ namespace Emby.Server.Implementations.IO
/// <inheritdoc /> /// <inheritdoc />
public virtual bool AreEqual(string path1, string path2) public virtual bool AreEqual(string path1, string path2)
{ {
return Path.TrimEndingDirectorySeparator(path1).Equals( if (string.IsNullOrWhiteSpace(path1) || string.IsNullOrWhiteSpace(path2))
Path.TrimEndingDirectorySeparator(path2), {
return false;
}
var normalized1 = Path.TrimEndingDirectorySeparator(path1);
var normalized2 = Path.TrimEndingDirectorySeparator(path2);
return string.Equals(
normalized1,
normalized2,
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
} }

View File

@@ -347,8 +347,8 @@ pli||pi|Pali|pali
pol||pl|Polish|polonais pol||pl|Polish|polonais
pon|||Pohnpeian|pohnpei pon|||Pohnpeian|pohnpei
por||pt|Portuguese|portugais por||pt|Portuguese|portugais
por||pt-pt|Portuguese (Portugal)|portugais (pt-pt) pop||pt-pt|Portuguese (Portugal)|portugais (pt-pt)
por||pt-br|Portuguese (Brazil)|portugais (pt-br) pob||pt-br|Portuguese (Brazil)|portugais (pt-br)
pra|||Prakrit languages|prâkrit, langues pra|||Prakrit languages|prâkrit, langues
pro|||Provençal, Old (to 1500)|provençal ancien (jusqu'à 1500) pro|||Provençal, Old (to 1500)|provençal ancien (jusqu'à 1500)
pus||ps|Pushto; Pashto|pachto pus||ps|Pushto; Pashto|pachto

View File

@@ -618,12 +618,18 @@ public sealed class BaseItemRepository
{ {
context.BaseItemProviders.Where(e => e.ItemId == entity.Id).ExecuteDelete(); context.BaseItemProviders.Where(e => e.ItemId == entity.Id).ExecuteDelete();
context.BaseItemImageInfos.Where(e => e.ItemId == entity.Id).ExecuteDelete(); context.BaseItemImageInfos.Where(e => e.ItemId == entity.Id).ExecuteDelete();
context.BaseItemMetadataFields.Where(e => e.ItemId == entity.Id).ExecuteDelete();
if (entity.Images is { Count: > 0 }) if (entity.Images is { Count: > 0 })
{ {
context.BaseItemImageInfos.AddRange(entity.Images); context.BaseItemImageInfos.AddRange(entity.Images);
} }
if (entity.LockedFields is { Count: > 0 })
{
context.BaseItemMetadataFields.AddRange(entity.LockedFields);
}
context.BaseItems.Attach(entity).State = EntityState.Modified; context.BaseItems.Attach(entity).State = EntityState.Modified;
} }
} }
@@ -1647,19 +1653,18 @@ public sealed class BaseItemRepository
var tags = filter.Tags.ToList(); var tags = filter.Tags.ToList();
var excludeTags = filter.ExcludeTags.ToList(); var excludeTags = filter.ExcludeTags.ToList();
if (filter.IsMovie == true) if (filter.IsMovie.HasValue)
{ {
if (filter.IncludeItemTypes.Length == 0 var shouldIncludeAllMovieTypes = filter.IsMovie.Value
|| filter.IncludeItemTypes.Contains(BaseItemKind.Movie) && (filter.IncludeItemTypes.Length == 0
|| filter.IncludeItemTypes.Contains(BaseItemKind.Trailer)) || filter.IncludeItemTypes.Contains(BaseItemKind.Movie)
|| filter.IncludeItemTypes.Contains(BaseItemKind.Trailer));
if (!shouldIncludeAllMovieTypes)
{ {
baseQuery = baseQuery.Where(e => e.IsMovie); baseQuery = baseQuery.Where(e => e.IsMovie == filter.IsMovie.Value);
} }
} }
else if (filter.IsMovie.HasValue)
{
baseQuery = baseQuery.Where(e => e.IsMovie == filter.IsMovie);
}
if (filter.IsSeries.HasValue) if (filter.IsSeries.HasValue)
{ {

View File

@@ -668,34 +668,22 @@ namespace MediaBrowser.Controller.Entities
} }
} }
var result = GetItems(new InternalItemsQuery(user) return LibraryManager.GetCount(new InternalItemsQuery(user)
{ {
Recursive = false, Recursive = false,
Limit = 0, Parent = this
Parent = this,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
}
}); });
return result.TotalRecordCount;
} }
public virtual int GetRecursiveChildCount(User user) public virtual int GetRecursiveChildCount(User user)
{ {
return GetItems(new InternalItemsQuery(user) return LibraryManager.GetCount(new InternalItemsQuery(user)
{ {
Recursive = true, Recursive = true,
Parent = this,
IsFolder = false, IsFolder = false,
IsVirtualItem = false, IsVirtualItem = false
EnableTotalRecordCount = true, });
Limit = 0,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
}
}).TotalRecordCount;
} }
public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query) public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query)
@@ -1409,7 +1397,7 @@ namespace MediaBrowser.Controller.Entities
if (this is BoxSet && (query.OrderBy is null || query.OrderBy.Count == 0)) if (this is BoxSet && (query.OrderBy is null || query.OrderBy.Count == 0))
{ {
realChildren = realChildren realChildren = realChildren
.OrderBy(e => e.ProductionYear ?? int.MaxValue) .OrderBy(e => e.PremiereDate ?? DateTime.MaxValue)
.ToArray(); .ToArray();
} }

View File

@@ -63,6 +63,29 @@ public static class FileSystemHelper
} }
} }
/// <summary>
/// Resolves a single link hop for the specified path.
/// </summary>
/// <remarks>
/// Returns <c>null</c> if the path is not a symbolic link or the filesystem does not support link resolution (e.g., exFAT).
/// </remarks>
/// <param name="path">The file path to resolve.</param>
/// <returns>
/// A <see cref="FileInfo"/> representing the next link target if the path is a link; otherwise, <c>null</c>.
/// </returns>
private static FileInfo? Resolve(string path)
{
try
{
return File.ResolveLinkTarget(path, returnFinalTarget: false) as FileInfo;
}
catch (IOException)
{
// Filesystem doesn't support links (e.g., exFAT).
return null;
}
}
/// <summary> /// <summary>
/// Gets the target of the specified file link. /// Gets the target of the specified file link.
/// </summary> /// </summary>
@@ -84,23 +107,26 @@ public static class FileSystemHelper
if (!returnFinalTarget) if (!returnFinalTarget)
{ {
return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo; return Resolve(linkPath);
} }
if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo) var targetInfo = Resolve(linkPath);
{ if (targetInfo is null || !targetInfo.Exists)
return null;
}
if (!targetInfo.Exists)
{ {
return targetInfo; return targetInfo;
} }
var currentPath = targetInfo.FullName; var currentPath = targetInfo.FullName;
var visited = new HashSet<string>(StringComparer.Ordinal) { linkPath, currentPath }; var visited = new HashSet<string>(StringComparer.Ordinal) { linkPath, currentPath };
while (File.ResolveLinkTarget(currentPath, returnFinalTarget: false) is FileInfo linkInfo)
while (true)
{ {
var linkInfo = Resolve(currentPath);
if (linkInfo is null)
{
break;
}
var targetPath = linkInfo.FullName; var targetPath = linkInfo.FullName;
// If an infinite loop is detected, return the file info for the // If an infinite loop is detected, return the file info for the

View File

@@ -2378,6 +2378,13 @@ namespace MediaBrowser.Controller.MediaEncoding
var requestHasSDR = requestedRangeTypes.Contains(VideoRangeType.SDR.ToString(), StringComparison.OrdinalIgnoreCase); var requestHasSDR = requestedRangeTypes.Contains(VideoRangeType.SDR.ToString(), StringComparison.OrdinalIgnoreCase);
var requestHasDOVI = requestedRangeTypes.Contains(VideoRangeType.DOVI.ToString(), StringComparison.OrdinalIgnoreCase); var requestHasDOVI = requestedRangeTypes.Contains(VideoRangeType.DOVI.ToString(), StringComparison.OrdinalIgnoreCase);
// If SDR is the only supported range, we should not copy any of the HDR streams.
// All the following copy check assumes at least one HDR format is supported.
if (requestedRangeTypes.Length == 1 && requestHasSDR && videoStream.VideoRangeType != VideoRangeType.SDR)
{
return false;
}
// If the client does not support DOVI and the video stream is DOVI without fallback, we should not copy it. // If the client does not support DOVI and the video stream is DOVI without fallback, we should not copy it.
if (!requestHasDOVI && videoStream.VideoRangeType == VideoRangeType.DOVI) if (!requestHasDOVI && videoStream.VideoRangeType == VideoRangeType.DOVI)
{ {

View File

@@ -1,3 +1,4 @@
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using BDInfo.IO; using BDInfo.IO;
@@ -58,6 +59,8 @@ public class BdInfoDirectoryInfo : IDirectoryInfo
} }
} }
private static bool IsHidden(ReadOnlySpan<char> name) => name.StartsWith('.');
/// <summary> /// <summary>
/// Gets the directories. /// Gets the directories.
/// </summary> /// </summary>
@@ -65,6 +68,7 @@ public class BdInfoDirectoryInfo : IDirectoryInfo
public IDirectoryInfo[] GetDirectories() public IDirectoryInfo[] GetDirectories()
{ {
return _fileSystem.GetDirectories(_impl.FullName) return _fileSystem.GetDirectories(_impl.FullName)
.Where(d => !IsHidden(d.Name))
.Select(x => new BdInfoDirectoryInfo(_fileSystem, x)) .Select(x => new BdInfoDirectoryInfo(_fileSystem, x))
.ToArray(); .ToArray();
} }
@@ -76,6 +80,7 @@ public class BdInfoDirectoryInfo : IDirectoryInfo
public IFileInfo[] GetFiles() public IFileInfo[] GetFiles()
{ {
return _fileSystem.GetFiles(_impl.FullName) return _fileSystem.GetFiles(_impl.FullName)
.Where(d => !IsHidden(d.Name))
.Select(x => new BdInfoFileInfo(x)) .Select(x => new BdInfoFileInfo(x))
.ToArray(); .ToArray();
} }
@@ -88,6 +93,7 @@ public class BdInfoDirectoryInfo : IDirectoryInfo
public IFileInfo[] GetFiles(string searchPattern) public IFileInfo[] GetFiles(string searchPattern)
{ {
return _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false) return _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false)
.Where(d => !IsHidden(d.Name))
.Select(x => new BdInfoFileInfo(x)) .Select(x => new BdInfoFileInfo(x))
.ToArray(); .ToArray();
} }
@@ -105,6 +111,7 @@ public class BdInfoDirectoryInfo : IDirectoryInfo
new[] { searchPattern }, new[] { searchPattern },
false, false,
searchOption == SearchOption.AllDirectories) searchOption == SearchOption.AllDirectories)
.Where(d => !IsHidden(d.Name))
.Select(x => new BdInfoFileInfo(x)) .Select(x => new BdInfoFileInfo(x))
.ToArray(); .ToArray();
} }

View File

@@ -511,7 +511,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
? "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_chapters -show_format" ? "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_chapters -show_format"
: "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_format"; : "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_format";
if (!isAudio && _proberSupportsFirstVideoFrame) if (protocol == MediaProtocol.File && !isAudio && _proberSupportsFirstVideoFrame)
{ {
args += " -show_frames -only_first_vframe"; args += " -show_frames -only_first_vframe";
} }

View File

@@ -151,9 +151,9 @@ namespace MediaBrowser.Providers.Manager
.ConfigureAwait(false); .ConfigureAwait(false);
updateType |= beforeSaveResult; updateType |= beforeSaveResult;
if (!isFirstRefresh) if (isFirstRefresh)
{ {
updateType = await SaveInternal(item, refreshOptions, updateType, isFirstRefresh, requiresRefresh, metadataResult, cancellationToken).ConfigureAwait(false); await SaveItemAsync(metadataResult, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
} }
// Next run metadata providers // Next run metadata providers