/src/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs

https://github.com/NzbDrone/NzbDrone · C# · 213 lines · 173 code · 40 blank · 0 comment · 15 complexity · 6244ef79a66f5459896f4060d9b998fb MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using NLog;
  6. using NzbDrone.Common.Disk;
  7. using NzbDrone.Common.EnsureThat;
  8. using NzbDrone.Common.Extensions;
  9. using NzbDrone.Core.Configuration;
  10. using NzbDrone.Core.MediaFiles.Events;
  11. using NzbDrone.Core.Messaging.Events;
  12. using NzbDrone.Core.Organizer;
  13. using NzbDrone.Core.Parser.Model;
  14. using NzbDrone.Core.Tv;
  15. using NzbDrone.Core.MediaFiles.EpisodeImport;
  16. namespace NzbDrone.Core.MediaFiles
  17. {
  18. public interface IMoveEpisodeFiles
  19. {
  20. EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series);
  21. EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
  22. EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
  23. }
  24. public class EpisodeFileMovingService : IMoveEpisodeFiles
  25. {
  26. private readonly IEpisodeService _episodeService;
  27. private readonly IUpdateEpisodeFileService _updateEpisodeFileService;
  28. private readonly IBuildFileNames _buildFileNames;
  29. private readonly IDiskTransferService _diskTransferService;
  30. private readonly IDiskProvider _diskProvider;
  31. private readonly IMediaFileAttributeService _mediaFileAttributeService;
  32. private readonly IEventAggregator _eventAggregator;
  33. private readonly IConfigService _configService;
  34. private readonly Logger _logger;
  35. public EpisodeFileMovingService(IEpisodeService episodeService,
  36. IUpdateEpisodeFileService updateEpisodeFileService,
  37. IBuildFileNames buildFileNames,
  38. IDiskTransferService diskTransferService,
  39. IDiskProvider diskProvider,
  40. IMediaFileAttributeService mediaFileAttributeService,
  41. IEventAggregator eventAggregator,
  42. IConfigService configService,
  43. Logger logger)
  44. {
  45. _episodeService = episodeService;
  46. _updateEpisodeFileService = updateEpisodeFileService;
  47. _buildFileNames = buildFileNames;
  48. _diskTransferService = diskTransferService;
  49. _diskProvider = diskProvider;
  50. _mediaFileAttributeService = mediaFileAttributeService;
  51. _eventAggregator = eventAggregator;
  52. _configService = configService;
  53. _logger = logger;
  54. }
  55. public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series)
  56. {
  57. var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
  58. var filePath = _buildFileNames.BuildFilePath(episodes, series, episodeFile, Path.GetExtension(episodeFile.RelativePath));
  59. EnsureEpisodeFolder(episodeFile, series, episodes.Select(v => v.SeasonNumber).First(), filePath);
  60. _logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath);
  61. return TransferFile(episodeFile, series, episodes, filePath, TransferMode.Move);
  62. }
  63. public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
  64. {
  65. var filePath = _buildFileNames.BuildFilePath(localEpisode.Episodes, localEpisode.Series, episodeFile, Path.GetExtension(localEpisode.Path));
  66. EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
  67. _logger.Debug("Moving episode file: {0} to {1}", episodeFile.Path, filePath);
  68. return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Move);
  69. }
  70. public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
  71. {
  72. var filePath = _buildFileNames.BuildFilePath(localEpisode.Episodes, localEpisode.Series, episodeFile, Path.GetExtension(localEpisode.Path));
  73. EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
  74. if (_configService.CopyUsingHardlinks)
  75. {
  76. _logger.Debug("Hardlinking episode file: {0} to {1}", episodeFile.Path, filePath);
  77. return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.HardLinkOrCopy);
  78. }
  79. _logger.Debug("Copying episode file: {0} to {1}", episodeFile.Path, filePath);
  80. return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Copy);
  81. }
  82. private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Episode> episodes, string destinationFilePath, TransferMode mode)
  83. {
  84. Ensure.That(episodeFile, () => episodeFile).IsNotNull();
  85. Ensure.That(series, () => series).IsNotNull();
  86. Ensure.That(destinationFilePath, () => destinationFilePath).IsValidPath();
  87. var episodeFilePath = episodeFile.Path ?? Path.Combine(series.Path, episodeFile.RelativePath);
  88. if (!_diskProvider.FileExists(episodeFilePath))
  89. {
  90. throw new FileNotFoundException("Episode file path does not exist", episodeFilePath);
  91. }
  92. if (episodeFilePath == destinationFilePath)
  93. {
  94. throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath);
  95. }
  96. _diskTransferService.TransferFile(episodeFilePath, destinationFilePath, mode);
  97. episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilePath);
  98. _updateEpisodeFileService.ChangeFileDateForFile(episodeFile, series, episodes);
  99. try
  100. {
  101. _mediaFileAttributeService.SetFolderLastWriteTime(series.Path, episodeFile.DateAdded);
  102. if (series.SeasonFolder)
  103. {
  104. var seasonFolder = Path.GetDirectoryName(destinationFilePath);
  105. _mediaFileAttributeService.SetFolderLastWriteTime(seasonFolder, episodeFile.DateAdded);
  106. }
  107. }
  108. catch (Exception ex)
  109. {
  110. _logger.Warn(ex, "Unable to set last write time");
  111. }
  112. _mediaFileAttributeService.SetFilePermissions(destinationFilePath);
  113. return episodeFile;
  114. }
  115. private void EnsureEpisodeFolder(EpisodeFile episodeFile, LocalEpisode localEpisode, string filePath)
  116. {
  117. EnsureEpisodeFolder(episodeFile, localEpisode.Series, localEpisode.SeasonNumber, filePath);
  118. }
  119. private void EnsureEpisodeFolder(EpisodeFile episodeFile, Series series, int seasonNumber, string filePath)
  120. {
  121. var episodeFolder = Path.GetDirectoryName(filePath);
  122. var seasonFolder = _buildFileNames.BuildSeasonPath(series, seasonNumber);
  123. var seriesFolder = series.Path;
  124. var rootFolder = new OsPath(seriesFolder).Directory.FullPath;
  125. if (!_diskProvider.FolderExists(rootFolder))
  126. {
  127. throw new RootFolderNotFoundException(string.Format("Root folder '{0}' was not found.", rootFolder));
  128. }
  129. var changed = false;
  130. var newEvent = new EpisodeFolderCreatedEvent(series, episodeFile);
  131. if (!_diskProvider.FolderExists(seriesFolder))
  132. {
  133. CreateFolder(seriesFolder);
  134. newEvent.SeriesFolder = seriesFolder;
  135. changed = true;
  136. }
  137. if (seriesFolder != seasonFolder && !_diskProvider.FolderExists(seasonFolder))
  138. {
  139. CreateFolder(seasonFolder);
  140. newEvent.SeasonFolder = seasonFolder;
  141. changed = true;
  142. }
  143. if (seasonFolder != episodeFolder && !_diskProvider.FolderExists(episodeFolder))
  144. {
  145. CreateFolder(episodeFolder);
  146. newEvent.EpisodeFolder = episodeFolder;
  147. changed = true;
  148. }
  149. if (changed)
  150. {
  151. _eventAggregator.PublishEvent(newEvent);
  152. }
  153. }
  154. private void CreateFolder(string directoryName)
  155. {
  156. Ensure.That(directoryName, () => directoryName).IsNotNullOrWhiteSpace();
  157. var parentFolder = new OsPath(directoryName).Directory.FullPath;
  158. if (!_diskProvider.FolderExists(parentFolder))
  159. {
  160. CreateFolder(parentFolder);
  161. }
  162. try
  163. {
  164. _diskProvider.CreateFolder(directoryName);
  165. }
  166. catch (IOException ex)
  167. {
  168. _logger.Error(ex, "Unable to create directory: {0}", directoryName);
  169. }
  170. _mediaFileAttributeService.SetFolderPermissions(directoryName);
  171. }
  172. }
  173. }