using System.IO;
namespace EmbyToolbox.Services;
/// Область применения snapshot: каталог эпизода и опционально общий корень батча.
public static class SnapshotScopePaths
{
private static readonly StringComparer IC = StringComparer.OrdinalIgnoreCase;
/// Абсолютный путь без завершающего разделителя.
public static string NormalizeScopeDirectory(string directoryPath)
{
if (string.IsNullOrWhiteSpace(directoryPath))
{
return string.Empty;
}
return Path.TrimEndingDirectorySeparator(Path.GetFullPath(directoryPath.Trim()));
}
public static string NormalizeScopeBatchRootNullable(string? path) =>
string.IsNullOrWhiteSpace(path) ? string.Empty : NormalizeScopeDirectory(path);
public static string GetVideoScopeDirectory(string videoFileFullPath)
{
var dir = Path.GetDirectoryName(Path.GetFullPath(videoFileFullPath));
return dir is null ? string.Empty : NormalizeScopeDirectory(dir);
}
/// Узкий общий предок каталогов всех указанных видеофайлов (полные пути).
public static string? TryGetLowestCommonAncestorDirectory(IReadOnlyList videoFileAbsolutePaths)
{
if (videoFileAbsolutePaths.Count == 0)
{
return null;
}
var dirs = videoFileAbsolutePaths
.Select(static p =>
{
var d = Path.GetDirectoryName(Path.GetFullPath(p));
return d is null ? string.Empty : NormalizeScopeDirectory(d);
})
.Where(static d => d.Length > 0)
.Distinct(IC)
.ToList();
if (dirs.Count == 0)
{
return null;
}
var acc = dirs[0];
for (var i = 1; i < dirs.Count; i++)
{
acc = LowestCommonAncestorOfTwoDirectories(acc, dirs[i]);
if (string.IsNullOrEmpty(acc))
{
return null;
}
}
return acc;
}
private static string LowestCommonAncestorOfTwoDirectories(string dirA, string dirB)
{
if (dirA.Length == 0 || dirB.Length == 0)
{
return string.Empty;
}
var ancestorsA = new HashSet(EnumerateAncestorDirectoriesInclusive(dirA), IC);
foreach (var cand in EnumerateAncestorDirectoriesInclusive(dirB))
{
if (ancestorsA.Contains(cand))
{
return cand;
}
}
return string.Empty;
}
/// От указанной папки вверх к корню включая сам каталог.
private static IEnumerable EnumerateAncestorDirectoriesInclusive(string normalizedAbsoluteDir)
{
var d = NormalizeScopeDirectory(normalizedAbsoluteDir);
while (!string.IsNullOrEmpty(d))
{
yield return d;
var parent = Path.GetDirectoryName(d);
if (string.IsNullOrEmpty(parent))
{
yield break;
}
parent = NormalizeScopeDirectory(parent);
if (string.Equals(parent, d, StringComparison.OrdinalIgnoreCase))
{
yield break;
}
d = parent;
}
}
}