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 />
public virtual bool AreEqual(string path1, string path2)
{
return Path.TrimEndingDirectorySeparator(path1).Equals(
Path.TrimEndingDirectorySeparator(path2),
if (string.IsNullOrWhiteSpace(path1) || string.IsNullOrWhiteSpace(path2))
{
return false;
}
var normalized1 = Path.TrimEndingDirectorySeparator(path1);
var normalized2 = Path.TrimEndingDirectorySeparator(path2);
return string.Equals(
normalized1,
normalized2,
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}

View File

@@ -347,8 +347,8 @@ pli||pi|Pali|pali
pol||pl|Polish|polonais
pon|||Pohnpeian|pohnpei
por||pt|Portuguese|portugais
por||pt-pt|Portuguese (Portugal)|portugais (pt-pt)
por||pt-br|Portuguese (Brazil)|portugais (pt-br)
pop||pt-pt|Portuguese (Portugal)|portugais (pt-pt)
pob||pt-br|Portuguese (Brazil)|portugais (pt-br)
pra|||Prakrit languages|prâkrit, langues
pro|||Provençal, Old (to 1500)|provençal ancien (jusqu'à 1500)
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.BaseItemImageInfos.Where(e => e.ItemId == entity.Id).ExecuteDelete();
context.BaseItemMetadataFields.Where(e => e.ItemId == entity.Id).ExecuteDelete();
if (entity.Images is { Count: > 0 })
{
context.BaseItemImageInfos.AddRange(entity.Images);
}
if (entity.LockedFields is { Count: > 0 })
{
context.BaseItemMetadataFields.AddRange(entity.LockedFields);
}
context.BaseItems.Attach(entity).State = EntityState.Modified;
}
}
@@ -1647,19 +1653,18 @@ public sealed class BaseItemRepository
var tags = filter.Tags.ToList();
var excludeTags = filter.ExcludeTags.ToList();
if (filter.IsMovie == true)
if (filter.IsMovie.HasValue)
{
if (filter.IncludeItemTypes.Length == 0
|| filter.IncludeItemTypes.Contains(BaseItemKind.Movie)
|| filter.IncludeItemTypes.Contains(BaseItemKind.Trailer))
var shouldIncludeAllMovieTypes = filter.IsMovie.Value
&& (filter.IncludeItemTypes.Length == 0
|| 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)
{

View File

@@ -668,34 +668,22 @@ namespace MediaBrowser.Controller.Entities
}
}
var result = GetItems(new InternalItemsQuery(user)
return LibraryManager.GetCount(new InternalItemsQuery(user)
{
Recursive = false,
Limit = 0,
Parent = this,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
}
Parent = this
});
return result.TotalRecordCount;
}
public virtual int GetRecursiveChildCount(User user)
{
return GetItems(new InternalItemsQuery(user)
return LibraryManager.GetCount(new InternalItemsQuery(user)
{
Recursive = true,
Parent = this,
IsFolder = false,
IsVirtualItem = false,
EnableTotalRecordCount = true,
Limit = 0,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
}
}).TotalRecordCount;
IsVirtualItem = false
});
}
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))
{
realChildren = realChildren
.OrderBy(e => e.ProductionYear ?? int.MaxValue)
.OrderBy(e => e.PremiereDate ?? DateTime.MaxValue)
.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>
/// Gets the target of the specified file link.
/// </summary>
@@ -84,23 +107,26 @@ public static class FileSystemHelper
if (!returnFinalTarget)
{
return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo;
return Resolve(linkPath);
}
if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo)
{
return null;
}
if (!targetInfo.Exists)
var targetInfo = Resolve(linkPath);
if (targetInfo is null || !targetInfo.Exists)
{
return targetInfo;
}
var currentPath = targetInfo.FullName;
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;
// 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 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 (!requestHasDOVI && videoStream.VideoRangeType == VideoRangeType.DOVI)
{

View File

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

View File

@@ -151,9 +151,9 @@ namespace MediaBrowser.Providers.Manager
.ConfigureAwait(false);
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