StackResolver.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #pragma warning disable CS1591
  2. #pragma warning disable SA1600
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text.RegularExpressions;
  8. using Emby.Naming.Common;
  9. using MediaBrowser.Model.IO;
  10. namespace Emby.Naming.Video
  11. {
  12. public class StackResolver
  13. {
  14. private readonly NamingOptions _options;
  15. public StackResolver(NamingOptions options)
  16. {
  17. _options = options;
  18. }
  19. public IEnumerable<FileStack> ResolveDirectories(IEnumerable<string> files)
  20. {
  21. return Resolve(files.Select(i => new FileSystemMetadata
  22. {
  23. FullName = i,
  24. IsDirectory = true
  25. }));
  26. }
  27. public IEnumerable<FileStack> ResolveFiles(IEnumerable<string> files)
  28. {
  29. return Resolve(files.Select(i => new FileSystemMetadata
  30. {
  31. FullName = i,
  32. IsDirectory = false
  33. }));
  34. }
  35. public IEnumerable<FileStack> ResolveAudioBooks(IEnumerable<FileSystemMetadata> files)
  36. {
  37. foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName)))
  38. {
  39. var stack = new FileStack()
  40. {
  41. Name = Path.GetFileName(directory.Key),
  42. IsDirectoryStack = false
  43. };
  44. foreach (var file in directory)
  45. {
  46. if (file.IsDirectory)
  47. {
  48. continue;
  49. }
  50. stack.Files.Add(file.FullName);
  51. }
  52. yield return stack;
  53. }
  54. }
  55. public IEnumerable<FileStack> Resolve(IEnumerable<FileSystemMetadata> files)
  56. {
  57. var resolver = new VideoResolver(_options);
  58. var list = files
  59. .Where(i => i.IsDirectory || resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName))
  60. .OrderBy(i => i.FullName)
  61. .ToList();
  62. var expressions = _options.VideoFileStackingRegexes;
  63. for (var i = 0; i < list.Count; i++)
  64. {
  65. var offset = 0;
  66. var file1 = list[i];
  67. var expressionIndex = 0;
  68. while (expressionIndex < expressions.Length)
  69. {
  70. var exp = expressions[expressionIndex];
  71. var stack = new FileStack();
  72. // (Title)(Volume)(Ignore)(Extension)
  73. var match1 = FindMatch(file1, exp, offset);
  74. if (match1.Success)
  75. {
  76. var title1 = match1.Groups[1].Value;
  77. var volume1 = match1.Groups[2].Value;
  78. var ignore1 = match1.Groups[3].Value;
  79. var extension1 = match1.Groups[4].Value;
  80. var j = i + 1;
  81. while (j < list.Count)
  82. {
  83. var file2 = list[j];
  84. if (file1.IsDirectory != file2.IsDirectory)
  85. {
  86. j++;
  87. continue;
  88. }
  89. // (Title)(Volume)(Ignore)(Extension)
  90. var match2 = FindMatch(file2, exp, offset);
  91. if (match2.Success)
  92. {
  93. var title2 = match2.Groups[1].Value;
  94. var volume2 = match2.Groups[2].Value;
  95. var ignore2 = match2.Groups[3].Value;
  96. var extension2 = match2.Groups[4].Value;
  97. if (string.Equals(title1, title2, StringComparison.OrdinalIgnoreCase))
  98. {
  99. if (!string.Equals(volume1, volume2, StringComparison.OrdinalIgnoreCase))
  100. {
  101. if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase)
  102. && string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase))
  103. {
  104. if (stack.Files.Count == 0)
  105. {
  106. stack.Name = title1 + ignore1;
  107. stack.IsDirectoryStack = file1.IsDirectory;
  108. stack.Files.Add(file1.FullName);
  109. }
  110. stack.Files.Add(file2.FullName);
  111. }
  112. else
  113. {
  114. // Sequel
  115. offset = 0;
  116. expressionIndex++;
  117. break;
  118. }
  119. }
  120. else if (!string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase))
  121. {
  122. // False positive, try again with offset
  123. offset = match1.Groups[3].Index;
  124. break;
  125. }
  126. else
  127. {
  128. // Extension mismatch
  129. offset = 0;
  130. expressionIndex++;
  131. break;
  132. }
  133. }
  134. else
  135. {
  136. // Title mismatch
  137. offset = 0;
  138. expressionIndex++;
  139. break;
  140. }
  141. }
  142. else
  143. {
  144. // No match 2, next expression
  145. offset = 0;
  146. expressionIndex++;
  147. break;
  148. }
  149. j++;
  150. }
  151. if (j == list.Count)
  152. {
  153. expressionIndex = expressions.Length;
  154. }
  155. }
  156. else
  157. {
  158. // No match 1
  159. offset = 0;
  160. expressionIndex++;
  161. }
  162. if (stack.Files.Count > 1)
  163. {
  164. yield return stack;
  165. i += stack.Files.Count - 1;
  166. break;
  167. }
  168. }
  169. }
  170. }
  171. private static string GetRegexInput(FileSystemMetadata file)
  172. {
  173. // For directories, dummy up an extension otherwise the expressions will fail
  174. var input = !file.IsDirectory
  175. ? file.FullName
  176. : file.FullName + ".mkv";
  177. return Path.GetFileName(input);
  178. }
  179. private static Match FindMatch(FileSystemMetadata input, Regex regex, int offset)
  180. {
  181. var regexInput = GetRegexInput(input);
  182. if (offset < 0 || offset >= regexInput.Length)
  183. {
  184. return Match.Empty;
  185. }
  186. return regex.Match(regexInput, offset);
  187. }
  188. }
  189. }