using System; using System.Collections.Generic; using System.IO; using System.Linq; using Emby.Naming.AudioBook; using Emby.Naming.Common; using MediaBrowser.Model.IO; namespace Emby.Naming.Video { /// /// Resolve from list of paths. /// public static class StackResolver { /// /// Resolves only directories from paths. /// /// List of paths. /// The naming options. /// Enumerable of directories. public static IEnumerable ResolveDirectories(IEnumerable files, NamingOptions namingOptions) { return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = true }), namingOptions); } /// /// Resolves only files from paths. /// /// List of paths. /// The naming options. /// Enumerable of files. public static IEnumerable ResolveFiles(IEnumerable files, NamingOptions namingOptions) { return Resolve(files.Select(i => new FileSystemMetadata { FullName = i, IsDirectory = false }), namingOptions); } /// /// Resolves audiobooks from paths. /// /// List of paths. /// Enumerable of directories. public static IEnumerable ResolveAudioBooks(IEnumerable 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; } } } /// /// Resolves videos from paths. /// /// List of paths. /// The naming options. /// Enumerable of videos. public static IEnumerable Resolve(IEnumerable 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(); 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 class StackMetadata { public StackMetadata(bool isDirectory, bool isNumerical, string partType) { Parts = new Dictionary(StringComparer.OrdinalIgnoreCase); IsDirectory = isDirectory; IsNumerical = isNumerical; PartType = partType; } public Dictionary Parts { get; } public bool IsDirectory { get; } public bool IsNumerical { get; } public string PartType { get; } public bool ContainsPart(string partNumber) => Parts.ContainsKey(partNumber); } } }