Files
pgsql-jellyfin/Emby.Naming/Video/StackResolver.cs
wjones 8421e3ad5c Suppress non-critical warnings, refactor Emby.Naming
Expanded .editorconfig to silence non-critical IDE/StyleCop warnings and updated Emby.Naming.csproj to prevent warnings-as-errors in Debug. Refactored codebase for modern C# style, removed unused code and unnecessary usings, and fixed formatting and StringComparison issues. Added detailed documentation on code style and warning suppression. Project now builds cleanly with only critical issues surfaced.
2026-02-21 12:48:04 -05:00

161 lines
6.2 KiB
C#

// <copyright file="StackResolver.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace Emby.Naming.Video
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Emby.Naming.AudioBook;
using Emby.Naming.Common;
using MediaBrowser.Model.IO;
/// <summary>
/// Resolve <see cref="FileStack"/> from list of paths.
/// </summary>
public static class StackResolver
{
/// <summary>
/// Resolves only directories from paths.
/// </summary>
/// <param name="files">List of paths.</param>
/// <param name="namingOptions">The naming options.</param>
/// <returns>Enumerable <see cref="FileStack"/> of directories.</returns>
public static IEnumerable<FileStack> ResolveDirectories(IEnumerable<string> files, NamingOptions namingOptions)
{
return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = true }), namingOptions);
}
/// <summary>
/// Resolves only files from paths.
/// </summary>
/// <param name="files">List of paths.</param>
/// <param name="namingOptions">The naming options.</param>
/// <returns>Enumerable <see cref="FileStack"/> of files.</returns>
public static IEnumerable<FileStack> ResolveFiles(IEnumerable<string> files, NamingOptions namingOptions)
{
return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = false }), namingOptions);
}
/// <summary>
/// Resolves audiobooks from paths.
/// </summary>
/// <param name="files">List of paths.</param>
/// <returns>Enumerable <see cref="FileStack"/> of directories.</returns>
public static IEnumerable<FileStack> ResolveAudioBooks(IEnumerable<AudioBookFileInfo> files)
{
var groupedDirectoryFiles = files.GroupBy(file => Path.GetDirectoryName(file.Path));
foreach (var directory in groupedDirectoryFiles)
{
if (string.IsNullOrEmpty(directory.Key))
{
foreach (var file in directory)
{
var stack = new FileStack(Path.GetFileNameWithoutExtension(file.Path), false, new[] { file.Path });
yield return stack;
}
}
else
{
var stack = new FileStack(Path.GetFileName(directory.Key), false, directory.Select(f => f.Path).ToArray());
yield return stack;
}
}
}
/// <summary>
/// Resolves videos from paths.
/// </summary>
/// <param name="files">List of paths.</param>
/// <param name="namingOptions">The naming options.</param>
/// <returns>Enumerable <see cref="FileStack"/> of videos.</returns>
public static IEnumerable<FileStack> Resolve(IEnumerable<FileSystemMetadata> files, NamingOptions namingOptions)
{
var potentialFiles = files
.Where(i => i.IsDirectory || VideoResolver.IsVideoFile(i.FullName, namingOptions) || VideoResolver.IsStubFile(i.FullName, namingOptions))
.OrderBy(i => i.FullName);
var potentialStacks = new Dictionary<string, StackMetadata>();
foreach (var file in potentialFiles)
{
var name = file.Name;
if (string.IsNullOrEmpty(name))
{
name = Path.GetFileName(file.FullName);
}
for (var i = 0; i < namingOptions.VideoFileStackingRules.Length; i++)
{
var rule = namingOptions.VideoFileStackingRules[i];
if (!rule.Match(name, out var stackParsingResult))
{
continue;
}
var stackName = stackParsingResult.Value.StackName;
var partNumber = stackParsingResult.Value.PartNumber;
var partType = stackParsingResult.Value.PartType;
if (!potentialStacks.TryGetValue(stackName, out var stackResult))
{
stackResult = new StackMetadata(file.IsDirectory, rule.IsNumerical, partType);
potentialStacks[stackName] = stackResult;
}
if (stackResult.Parts.Count > 0)
{
if (stackResult.IsDirectory != file.IsDirectory
|| !string.Equals(partType, stackResult.PartType, StringComparison.OrdinalIgnoreCase)
|| stackResult.ContainsPart(partNumber))
{
continue;
}
if (rule.IsNumerical != stackResult.IsNumerical)
{
break;
}
}
stackResult.Parts.Add(partNumber, file);
break;
}
}
foreach (var (fileName, stack) in potentialStacks)
{
if (stack.Parts.Count < 2)
{
continue;
}
yield return new FileStack(fileName, stack.IsDirectory, stack.Parts.Select(kv => kv.Value.FullName).ToArray());
}
}
private sealed class StackMetadata
{
public StackMetadata(bool isDirectory, bool isNumerical, string partType)
{
this.Parts = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
this.IsDirectory = isDirectory;
this.IsNumerical = isNumerical;
this.PartType = partType;
}
public Dictionary<string, FileSystemMetadata> Parts { get; }
public bool IsDirectory { get; }
public bool IsNumerical { get; }
public string PartType { get; }
public bool ContainsPart(string partNumber) => Parts.ContainsKey(partNumber);
}
}
}