TvFileSorter.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using MediaBrowser.Controller.Entities.TV;
  2. using MediaBrowser.Controller.Library;
  3. using MediaBrowser.Controller.Providers;
  4. using MediaBrowser.Controller.Resolvers;
  5. using MediaBrowser.Model.Configuration;
  6. using MediaBrowser.Model.Entities;
  7. using MediaBrowser.Model.Logging;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. namespace MediaBrowser.Server.Implementations.FileSorting
  13. {
  14. public class TvFileSorter
  15. {
  16. private readonly ILibraryManager _libraryManager;
  17. private readonly ILogger _logger;
  18. public TvFileSorter(ILibraryManager libraryManager, ILogger logger)
  19. {
  20. _libraryManager = libraryManager;
  21. _logger = logger;
  22. }
  23. public void Sort(string path, FileSortingOptions options)
  24. {
  25. var minFileBytes = options.MinFileSizeMb * 1024 * 1024;
  26. var allSeries = _libraryManager.RootFolder
  27. .RecursiveChildren.OfType<Series>()
  28. .Where(i => i.LocationType == LocationType.FileSystem)
  29. .ToList();
  30. var eligibleFiles = new DirectoryInfo(path)
  31. .EnumerateFiles("*", SearchOption.AllDirectories)
  32. .Where(i => EntityResolutionHelper.IsVideoFile(i.FullName) && i.Length >= minFileBytes)
  33. .ToList();
  34. foreach (var file in eligibleFiles)
  35. {
  36. SortFile(file.FullName, options, allSeries);
  37. }
  38. if (options.LeftOverFileExtensionsToDelete.Length > 0)
  39. {
  40. DeleteLeftOverFiles(path, options.LeftOverFileExtensionsToDelete);
  41. }
  42. if (options.DeleteEmptyFolders)
  43. {
  44. DeleteEmptyFolders(path);
  45. }
  46. }
  47. private void SortFile(string path, FileSortingOptions options, IEnumerable<Series> allSeries)
  48. {
  49. _logger.Info("Sorting file {0}", path);
  50. var seriesName = TVUtils.GetSeriesNameFromEpisodeFile(path);
  51. if (!string.IsNullOrEmpty(seriesName))
  52. {
  53. var season = TVUtils.GetSeasonNumberFromEpisodeFile(path);
  54. if (season.HasValue)
  55. {
  56. // Passing in true will include a few extra regex's
  57. var episode = TVUtils.GetEpisodeNumberFromFile(path, true);
  58. if (episode.HasValue)
  59. {
  60. _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, season, episode);
  61. SortFile(path, seriesName, season.Value, episode.Value, options, allSeries);
  62. }
  63. else
  64. {
  65. _logger.Warn("Unable to determine episode number from {0}", path);
  66. }
  67. }
  68. else
  69. {
  70. _logger.Warn("Unable to determine season number from {0}", path);
  71. }
  72. }
  73. else
  74. {
  75. _logger.Warn("Unable to determine series name from {0}", path);
  76. }
  77. }
  78. private void SortFile(string path, string seriesName, int seasonNumber, int episodeNumber, FileSortingOptions options, IEnumerable<Series> allSeries)
  79. {
  80. var series = GetMatchingSeries(seriesName, allSeries);
  81. if (series == null)
  82. {
  83. _logger.Warn("Unable to find series in library matching name {0}", seriesName);
  84. return;
  85. }
  86. _logger.Info("Sorting file {0} into series {1}", path, series.Path);
  87. // Proceed to sort the file
  88. }
  89. private Series GetMatchingSeries(string seriesName, IEnumerable<Series> allSeries)
  90. {
  91. int? yearInName;
  92. var nameWithoutYear = seriesName;
  93. NameParser.ParseName(nameWithoutYear, out nameWithoutYear, out yearInName);
  94. return allSeries.Select(i => GetMatchScore(nameWithoutYear, yearInName, i))
  95. .Where(i => i.Item2 > 0)
  96. .OrderByDescending(i => i.Item2)
  97. .Select(i => i.Item1)
  98. .FirstOrDefault();
  99. }
  100. private Tuple<Series, int> GetMatchScore(string sortedName, int? year, Series series)
  101. {
  102. var score = 0;
  103. if (year.HasValue)
  104. {
  105. if (series.ProductionYear.HasValue && year.Value == series.ProductionYear.Value)
  106. {
  107. score++;
  108. }
  109. }
  110. // TODO: Improve this
  111. if (string.Equals(sortedName, series.Name, StringComparison.OrdinalIgnoreCase))
  112. {
  113. score++;
  114. }
  115. return new Tuple<Series, int>(series, score);
  116. }
  117. private void DeleteLeftOverFiles(string path, IEnumerable<string> extensions)
  118. {
  119. var eligibleFiles = new DirectoryInfo(path)
  120. .EnumerateFiles("*", SearchOption.AllDirectories)
  121. .Where(i => extensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
  122. .ToList();
  123. foreach (var file in eligibleFiles)
  124. {
  125. try
  126. {
  127. File.Delete(file.FullName);
  128. }
  129. catch (IOException ex)
  130. {
  131. _logger.ErrorException("Error deleting file {0}", ex, file.FullName);
  132. }
  133. }
  134. }
  135. private void DeleteEmptyFolders(string path)
  136. {
  137. try
  138. {
  139. foreach (var d in Directory.EnumerateDirectories(path))
  140. {
  141. DeleteEmptyFolders(d);
  142. }
  143. var entries = Directory.EnumerateFileSystemEntries(path);
  144. if (!entries.Any())
  145. {
  146. try
  147. {
  148. Directory.Delete(path);
  149. }
  150. catch (UnauthorizedAccessException) { }
  151. catch (DirectoryNotFoundException) { }
  152. }
  153. }
  154. catch (UnauthorizedAccessException) { }
  155. }
  156. }
  157. }