AudioBookListResolver.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using Emby.Naming.Common;
  6. using Emby.Naming.Video;
  7. using MediaBrowser.Model.IO;
  8. namespace Emby.Naming.AudioBook
  9. {
  10. /// <summary>
  11. /// Class used to resolve Name, Year, alternative files and extras from stack of files.
  12. /// </summary>
  13. public class AudioBookListResolver
  14. {
  15. private readonly NamingOptions _options;
  16. /// <summary>
  17. /// Initializes a new instance of the <see cref="AudioBookListResolver"/> class.
  18. /// </summary>
  19. /// <param name="options">Naming options passed along to <see cref="AudioBookResolver"/> and <see cref="AudioBookNameParser"/>.</param>
  20. public AudioBookListResolver(NamingOptions options)
  21. {
  22. _options = options;
  23. }
  24. /// <summary>
  25. /// Resolves Name, Year and differentiate alternative files and extras from regular audiobook files.
  26. /// </summary>
  27. /// <param name="files">List of files related to audiobook.</param>
  28. /// <returns>Returns IEnumerable of <see cref="AudioBookInfo"/>.</returns>
  29. public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files)
  30. {
  31. var audioBookResolver = new AudioBookResolver(_options);
  32. // File with empty fullname will be sorted out here.
  33. var audiobookFileInfos = files
  34. .Select(i => audioBookResolver.Resolve(i.FullName))
  35. .OfType<AudioBookFileInfo>()
  36. .ToList();
  37. var stackResult = new StackResolver(_options)
  38. .ResolveAudioBooks(audiobookFileInfos);
  39. foreach (var stack in stackResult)
  40. {
  41. var stackFiles = stack.Files
  42. .Select(i => audioBookResolver.Resolve(i))
  43. .OfType<AudioBookFileInfo>()
  44. .ToList();
  45. stackFiles.Sort();
  46. var nameParserResult = new AudioBookNameParser(_options).Parse(stack.Name);
  47. FindExtraAndAlternativeFiles(ref stackFiles, out var extras, out var alternativeVersions, nameParserResult);
  48. var info = new AudioBookInfo(
  49. nameParserResult.Name,
  50. nameParserResult.Year,
  51. stackFiles,
  52. extras,
  53. alternativeVersions);
  54. yield return info;
  55. }
  56. }
  57. private void FindExtraAndAlternativeFiles(ref List<AudioBookFileInfo> stackFiles, out List<AudioBookFileInfo> extras, out List<AudioBookFileInfo> alternativeVersions, AudioBookNameParserResult nameParserResult)
  58. {
  59. extras = new List<AudioBookFileInfo>();
  60. alternativeVersions = new List<AudioBookFileInfo>();
  61. var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber != null || x.PartNumber != null);
  62. var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
  63. var nameWithReplacedDots = nameParserResult.Name.Replace(' ', '.');
  64. foreach (var group in groupedBy)
  65. {
  66. if (group.Key.ChapterNumber == null && group.Key.PartNumber == null)
  67. {
  68. if (group.Count() > 1 || haveChaptersOrPages)
  69. {
  70. var ex = new List<AudioBookFileInfo>();
  71. var alt = new List<AudioBookFileInfo>();
  72. foreach (var audioFile in group)
  73. {
  74. var name = Path.GetFileNameWithoutExtension(audioFile.Path);
  75. if (name.Equals("audiobook") ||
  76. name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) ||
  77. name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
  78. {
  79. alt.Add(audioFile);
  80. }
  81. else
  82. {
  83. ex.Add(audioFile);
  84. }
  85. }
  86. if (ex.Count > 0)
  87. {
  88. var extra = ex
  89. .OrderBy(x => x.Container)
  90. .ThenBy(x => x.Path)
  91. .ToList();
  92. stackFiles = stackFiles.Except(extra).ToList();
  93. extras.AddRange(extra);
  94. }
  95. if (alt.Count > 0)
  96. {
  97. var alternatives = alt
  98. .OrderBy(x => x.Container)
  99. .ThenBy(x => x.Path)
  100. .ToList();
  101. var main = FindMainAudioBookFile(alternatives, nameParserResult.Name);
  102. alternatives.Remove(main);
  103. stackFiles = stackFiles.Except(alternatives).ToList();
  104. alternativeVersions.AddRange(alternatives);
  105. }
  106. }
  107. }
  108. else if (group.Count() > 1)
  109. {
  110. var alternatives = group
  111. .OrderBy(x => x.Container)
  112. .ThenBy(x => x.Path)
  113. .Skip(1)
  114. .ToList();
  115. stackFiles = stackFiles.Except(alternatives).ToList();
  116. alternativeVersions.AddRange(alternatives);
  117. }
  118. }
  119. }
  120. private AudioBookFileInfo FindMainAudioBookFile(List<AudioBookFileInfo> files, string name)
  121. {
  122. var main = files.Find(x => Path.GetFileNameWithoutExtension(x.Path).Equals(name, StringComparison.OrdinalIgnoreCase));
  123. main ??= files.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.Path).Equals("audiobook", StringComparison.OrdinalIgnoreCase));
  124. main ??= files.OrderBy(x => x.Container)
  125. .ThenBy(x => x.Path)
  126. .First();
  127. return main;
  128. }
  129. }
  130. }