From d3f823734e2c96fd3a89dae06f6b7dbda4993b2c Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 18:59:31 -0700 Subject: [PATCH 1/8] Cleanedup get GetNewFilename --- NzbDrone.Core/Providers/DiskScanProvider.cs | 428 ++++++++++++++++++ NzbDrone.Core/Providers/EpisodeProvider.cs | 2 +- .../Providers/Jobs/RenameEpisodeJob.cs | 3 +- NzbDrone.Core/Providers/MediaFileProvider.cs | 144 ++---- 4 files changed, 477 insertions(+), 100 deletions(-) create mode 100644 NzbDrone.Core/Providers/DiskScanProvider.cs diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs new file mode 100644 index 000000000..0483d14d2 --- /dev/null +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -0,0 +1,428 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Ninject; +using NLog; +using NzbDrone.Core.Helpers; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using PetaPoco; + +namespace NzbDrone.Core.Providers +{ + class DiskScanProvider + { + + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly string[] MediaExtentions = new[] { ".mkv", ".avi", ".wmv", ".mp4" }; + private readonly DiskProvider _diskProvider; + private readonly EpisodeProvider _episodeProvider; + private readonly SeriesProvider _seriesProvider; + private readonly ConfigProvider _configProvider; + private readonly IDatabase _database; + + [Inject] + public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider, + SeriesProvider seriesProvider, ConfigProvider configProvider, + IDatabase database) + { + _diskProvider = diskProvider; + _episodeProvider = episodeProvider; + _seriesProvider = seriesProvider; + _configProvider = configProvider; + _database = database; + } + + + public DiskScanProvider() + { + + } + + + /// + /// Scans the specified series folder for media files + /// + /// The series to be scanned + public virtual List Scan(Series series) + { + if (_episodeProvider.GetEpisodeBySeries(series.SeriesId).Count == 0) + { + Logger.Debug("Series {0} has no episodes. skipping", series.Title); + return new List(); + } + + var mediaFileList = GetVideoFiles(series.Path); + var fileList = new List(); + + foreach (var filePath in mediaFileList) + { + var file = ImportFile(series, filePath); + if (file != null) + fileList.Add(file); + } + + series.LastDiskSync = DateTime.Now; + _seriesProvider.UpdateSeries(series); + + return fileList; + } + + public virtual EpisodeFile ImportFile(Series series, string filePath) + { + Logger.Trace("Importing file to database [{0}]", filePath); + + try + { + var size = _diskProvider.GetSize(filePath); + + //If Size is less than 50MB and contains sample. Check for Size to ensure its not an episode with sample in the title + if (size < 40000000 && filePath.ToLower().Contains("sample")) + { + Logger.Trace("[{0}] appears to be a sample. skipping.", filePath); + return null; + } + + //Check to see if file already exists in the database + if (!_database.Exists("Path =@0", Parser.NormalizePath(filePath))) + { + var parseResult = Parser.ParseEpisodeInfo(filePath); + + if (parseResult == null) + return null; + + parseResult.CleanTitle = series.Title;//replaces the nasty path as title to help with logging + + //Stores the list of episodes to add to the EpisodeFile + var episodes = new List(); + + //Check for daily shows + if (parseResult.EpisodeNumbers == null) + { + var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.AirDate.Date); + + if (episode != null) + { + episodes.Add(episode); + } + else + { + Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); + } + } + else + { + foreach (var episodeNumber in parseResult.EpisodeNumbers) + { + var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.SeasonNumber, + episodeNumber); + + if (episode != null) + { + episodes.Add(episode); + } + else + { + Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); + } + } + } + + //Return null if no Episodes exist in the DB for the parsed episodes from file + if (episodes.Count <= 0) + return null; + + var episodeFile = new EpisodeFile(); + episodeFile.DateAdded = DateTime.Now; + episodeFile.SeriesId = series.SeriesId; + episodeFile.Path = Parser.NormalizePath(filePath); + episodeFile.Size = size; + episodeFile.Quality = parseResult.Quality.QualityType; + episodeFile.Proper = parseResult.Quality.Proper; + episodeFile.SeasonNumber = parseResult.SeasonNumber; + var fileId = Convert.ToInt32(_database.Insert(episodeFile)); + + //This is for logging + updating the episodes that are linked to this EpisodeFile + string episodeList = String.Empty; + foreach (var ep in episodes) + { + ep.EpisodeFileId = fileId; + _episodeProvider.UpdateEpisode(ep); + episodeList += String.Format(", {0}", ep.EpisodeId).Trim(' ', ','); + } + Logger.Trace("File {0}:{1} attached to episode(s): '{2}'", episodeFile.EpisodeFileId, filePath, + episodeList); + + return episodeFile; + } + + Logger.Trace("[{0}] already exists in the database. skipping.", filePath); + } + catch (Exception ex) + { + Logger.ErrorException("An error has occurred while importing file " + filePath, ex); + throw; + } + return null; + } + + /// + /// Removes files that no longer exist from the database + /// + /// list of files to verify + public virtual void CleanUp(List files) + { + //TODO: remove orphaned files. in files table but not linked to from episode table. + foreach (var episodeFile in files) + { + if (!_diskProvider.FileExists(episodeFile.Path)) + { + Logger.Trace("File {0} no longer exists on disk. removing from database.", episodeFile.Path); + + //Set the EpisodeFileId for each episode attached to this file to 0 + foreach (var episode in episodeFile.Episodes) + { + episode.EpisodeFileId = 0; + _episodeProvider.UpdateEpisode(episode); + } + + //Delete it from the DB + _database.Delete(episodeFile.EpisodeFileId); + } + } + } + + + + private List GetVideoFiles(string path) + { + Logger.Debug("Scanning '{0}' for episodes", path); + + var filesOnDisk = _diskProvider.GetFiles(path, "*.*", SearchOption.AllDirectories); + + var mediaFileList = filesOnDisk.Where(c => MediaExtentions.Contains(Path.GetExtension(c).ToLower())).ToList(); + + Logger.Debug("{0} media files were found in {1}", mediaFileList.Count, path); + return mediaFileList; + } + + public virtual List ImportNewFiles(string path, Series series) + { + var result = new List(); + + //Get all the files except those that are considered samples + var files = GetVideoFiles(path).Where(f => _diskProvider.GetSize(f) > 40000000 || !f.ToLower().Contains("sample")).ToList(); + + foreach (var file in files) + { + try + { + //Parse the filename + var parseResult = Parser.ParseEpisodeInfo(Path.GetFileName(file)); + parseResult.Series = series; + parseResult.Episodes = _episodeProvider.GetEpisodes(parseResult); + + if (parseResult.Episodes.Count == 0) + { + Logger.Error("File '{0}' contains invalid episode information, skipping import", file); + continue; + } + + var ext = _diskProvider.GetExtension(file); + var filename = GetNewFilename(parseResult.Episodes, series.Title, parseResult.Quality.QualityType) + ext; + var folder = series.Path + Path.DirectorySeparatorChar; + if (_configProvider.UseSeasonFolder) + folder += _configProvider.SeasonFolderFormat + .Replace("%0s", parseResult.SeasonNumber.ToString("00")) + .Replace("%s", parseResult.SeasonNumber.ToString()) + + Path.DirectorySeparatorChar; + + _diskProvider.CreateDirectory(folder); + + //Get a list of episodeFiles that we need to delete and cleanup + var episodeFilesToClean = new List(); + + foreach (var episode in parseResult.Episodes) + { + if (episode.EpisodeFileId > 0) + episodeFilesToClean.Add(episode.EpisodeFile); + } + + if (episodeFilesToClean.Count != episodeFilesToClean.Where(e => parseResult.Quality.QualityType >= e.Quality).Count()) + { + Logger.Debug("Episode isn't an upgrade for all episodes in file: [{0}]. Skipping.", file); + continue; + } + + //Delete the files and then cleanup! + foreach (var e in episodeFilesToClean) + { + if (_diskProvider.FileExists(e.Path)) + _diskProvider.DeleteFile(e.Path); + } + + CleanUp(episodeFilesToClean); + + //Move the file + _diskProvider.RenameFile(file, folder + filename); + + //Import into DB + result.Add(ImportFile(series, folder + filename)); + } + + catch (Exception ex) + { + Logger.WarnException("Error importing new download: " + file, ex); + } + } + + //If we have imported all the non-sample files, delete the folder, requires a minimum of 1 file to be imported. + if (files.Count() > 0 && files.Count() == result.Count) + { + Logger.Debug("All non-sample files have been processed, deleting folder: {0}", path); + _diskProvider.DeleteFolder(path, true); + } + + return result; + } + + public virtual string GetNewFilename(IList episodes, string seriesName, QualityTypes quality) + { + var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SeparatorStyle); + var numberStyle = EpisodeSortingHelper.GetNumberStyle(_configProvider.NumberStyle); + var useSeriesName = _configProvider.SeriesName; + var useEpisodeName = _configProvider.EpisodeName; + var replaceSpaces = _configProvider.ReplaceSpaces; + var appendQuality = _configProvider.AppendQuality; + + var title = String.Empty; + + if (episodes.Count == 1) + { + if (useSeriesName) + { + title += seriesName; + title += separatorStyle.Pattern; + } + + title += numberStyle.Pattern.Replace("%s", String.Format("{0}", episodes[0].SeasonNumber)) + .Replace("%0s", String.Format("{0:00}", episodes[0].SeasonNumber)) + .Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); + + if (useEpisodeName) + { + title += separatorStyle.Pattern; + title += episodes[0].Title; + } + + if (appendQuality) + title += String.Format(" [{0}]", quality); + + if (replaceSpaces) + title = title.Replace(' ', '.'); + + Logger.Debug("New File Name is: {0}", title); + return title; + } + + var multiEpisodeStyle = EpisodeSortingHelper.GetMultiEpisodeStyle(_configProvider.MultiEpisodeStyle); + + if (useSeriesName) + { + title += seriesName; + title += separatorStyle.Pattern; + } + + title += numberStyle.Pattern.Replace("%s", String.Format("{0}", episodes[0].SeasonNumber)) + .Replace("%0s", String.Format("{0:00}", episodes[0].SeasonNumber)) + .Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); + + var numbers = String.Empty; + var episodeNames = episodes[0].Title; + + for (int i = 1; i < episodes.Count; i++) + { + var episode = episodes[i]; + + if (multiEpisodeStyle.Name == "Duplicate") + { + numbers += separatorStyle.Pattern + numberStyle.Pattern.Replace("%s", String.Format("{0}", episode.SeasonNumber)) + .Replace("%0s", String.Format("{0:00}", episode.SeasonNumber)) + .Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber)); + } + else + { + numbers += multiEpisodeStyle.Pattern.Replace("%s", String.Format("{0}", episode.SeasonNumber)) + .Replace("%0s", String.Format("{0:00}", episode.SeasonNumber)) + .Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber)) + .Replace("%x", numberStyle.EpisodeSeparator) + .Replace("%p", separatorStyle.Pattern); + } + + episodeNames += String.Format(" + {0}", episode.Title); + } + + title += numbers; + + if (useEpisodeName) + { + episodeNames = episodeNames.TrimEnd(' ', '+'); + + title += separatorStyle.Pattern; + title += episodeNames; + } + + if (appendQuality) + title += String.Format(" [{0}]", quality); + + if (replaceSpaces) + title = title.Replace(' ', '.'); + + Logger.Debug("New File Name is: {0}", title); + return title; + } + + public virtual bool RenameEpisodeFile(int episodeFileId, ProgressNotification notification) + { + var episodeFile = GetEpisodeFile(episodeFileId); + + if (episodeFile == null) + return false; + + try + { + notification.CurrentMessage = String.Format("Renaming '{0}'", episodeFile.Path); + + var series = _seriesProvider.GetSeries(episodeFile.SeriesId); + var folder = new FileInfo(episodeFile.Path).DirectoryName; + var episodes = _episodeProvider.EpisodesByFileId(episodeFileId); + var ext = _diskProvider.GetExtension(episodeFile.Path); + + var newFileName = GetNewFilename(episodes, series.Title, episodeFile.Quality); + + var newFile = folder + Path.DirectorySeparatorChar + newFileName + ext; + + //Do the rename + _diskProvider.RenameFile(episodeFile.Path, newFile); + + //Update the filename in the DB + episodeFile.Path = newFile; + Update(episodeFile); + + notification.CurrentMessage = String.Format("Finished Renaming '{0}'", newFile); + } + + catch (Exception e) + { + notification.CurrentMessage = String.Format("Failed to Rename '{0}'", episodeFile.Path); + Logger.ErrorException("An error has occurred while renaming episode: " + episodeFile.Path, e); + throw; + } + return true; + } + } +} diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 10825b9a2..006d1d8e9 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -91,7 +91,7 @@ namespace NzbDrone.Core.Providers return AttachSeries(episodes.ToList()); } - public virtual IList EpisodesByFileId(int episodeFileId) + public virtual IList GetEpisodesByFileId(int episodeFileId) { return AttachSeries(_database.Fetch("WHERE EpisodeFileId = @0", episodeFileId)); } diff --git a/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs b/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs index e8184750e..e9f4373d9 100644 --- a/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs +++ b/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs @@ -36,7 +36,8 @@ namespace NzbDrone.Core.Providers.Jobs public void Start(ProgressNotification notification, int targetId) { - _mediaFileProvider.RenameEpisodeFile(targetId, notification); + var episode = _mediaFileProvider.GetEpisodeFile(targetId); + _mediaFileProvider.RenameEpisodeFile(episode); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index 8a25655df..34c6d81fe 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -192,7 +192,7 @@ namespace NzbDrone.Core.Providers public virtual void Update(EpisodeFile episodeFile) { _database.Update(episodeFile); - } + } public virtual EpisodeFile GetEpisodeFile(int episodeFileId) { @@ -315,134 +315,82 @@ namespace NzbDrone.Core.Providers { var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SeparatorStyle); var numberStyle = EpisodeSortingHelper.GetNumberStyle(_configProvider.NumberStyle); - var useSeriesName = _configProvider.SeriesName; - var useEpisodeName = _configProvider.EpisodeName; - var replaceSpaces = _configProvider.ReplaceSpaces; - var appendQuality = _configProvider.AppendQuality; - - var title = String.Empty; - if (episodes.Count == 1) - { - if (useSeriesName) - { - title += seriesName; - title += separatorStyle.Pattern; - } - - title += numberStyle.Pattern.Replace("%s", String.Format("{0}", episodes[0].SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episodes[0].SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); - - if (useEpisodeName) - { - title += separatorStyle.Pattern; - title += episodes[0].Title; - } - - if (appendQuality) - title += String.Format(" [{0}]", quality); - - if (replaceSpaces) - title = title.Replace(' ', '.'); - - Logger.Debug("New File Name is: {0}", title); - return title; - } + var episodeNames = episodes[0].Title; - var multiEpisodeStyle = EpisodeSortingHelper.GetMultiEpisodeStyle(_configProvider.MultiEpisodeStyle); + var result = String.Empty; - if (useSeriesName) + if (_configProvider.SeriesName) { - title += seriesName; - title += separatorStyle.Pattern; + result += seriesName + separatorStyle.Pattern; } - title += numberStyle.Pattern.Replace("%s", String.Format("{0}", episodes[0].SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episodes[0].SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); - - var numbers = String.Empty; - var episodeNames = episodes[0].Title; + result += numberStyle.Pattern.Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); - for (int i = 1; i < episodes.Count; i++) + if (episodes.Count > 1) { - var episode = episodes[i]; + var multiEpisodeStyle = EpisodeSortingHelper.GetMultiEpisodeStyle(_configProvider.MultiEpisodeStyle); - if (multiEpisodeStyle.Name == "Duplicate") - { - numbers += separatorStyle.Pattern + numberStyle.Pattern.Replace("%s", String.Format("{0}", episode.SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episode.SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber)); - } - else + foreach (var episode in episodes.OrderBy(e => e.EpisodeNumber).Skip(1)) { - numbers += multiEpisodeStyle.Pattern.Replace("%s", String.Format("{0}", episode.SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episode.SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber)) - .Replace("%x", numberStyle.EpisodeSeparator) - .Replace("%p", separatorStyle.Pattern); - } + if (multiEpisodeStyle.Name == "Duplicate") + { + result += separatorStyle.Pattern + numberStyle.Pattern; + } + else + { + result += multiEpisodeStyle.Pattern; + } - episodeNames += String.Format(" + {0}", episode.Title); + result = result.Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber)); + episodeNames += String.Format(" + {0}", episode.Title); + } } - title += numbers; + result = result + .Replace("%s", String.Format("{0}", episodes.First().SeasonNumber)) + .Replace("%0s", String.Format("{0:00}", episodes.First().SeasonNumber)) + .Replace("%x", numberStyle.EpisodeSeparator) + .Replace("%p", separatorStyle.Pattern); - if (useEpisodeName) + if (_configProvider.EpisodeName) { episodeNames = episodeNames.TrimEnd(' ', '+'); - - title += separatorStyle.Pattern; - title += episodeNames; + result += separatorStyle.Pattern + episodeNames; } - if (appendQuality) - title += String.Format(" [{0}]", quality); + if (_configProvider.AppendQuality) + result += String.Format(" [{0}]", quality); - if (replaceSpaces) - title = title.Replace(' ', '.'); + if (_configProvider.ReplaceSpaces) + result = result.Replace(' ', '.'); - Logger.Debug("New File Name is: {0}", title); - return title; + Logger.Debug("New File Name is: {0}", result.Trim()); + return result.Trim(); } - public virtual bool RenameEpisodeFile(int episodeFileId, ProgressNotification notification) + public virtual bool RenameEpisodeFile(EpisodeFile episodeFile) { - var episodeFile = GetEpisodeFile(episodeFileId); - if (episodeFile == null) - return false; - - try - { - notification.CurrentMessage = String.Format("Renaming '{0}'", episodeFile.Path); + throw new ArgumentNullException("episodeFile"); - var series = _seriesProvider.GetSeries(episodeFile.SeriesId); - var folder = new FileInfo(episodeFile.Path).DirectoryName; - var episodes = _episodeProvider.EpisodesByFileId(episodeFileId); - var ext = _diskProvider.GetExtension(episodeFile.Path); + var series = _seriesProvider.GetSeries(episodeFile.SeriesId); + var folder = new FileInfo(episodeFile.Path).DirectoryName; + var ext = _diskProvider.GetExtension(episodeFile.Path); + var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId); - var newFileName = GetNewFilename(episodes, series.Title, episodeFile.Quality); + var newFileName = GetNewFilename(episodes, series.Title, episodeFile.Quality); - var newFile = folder + Path.DirectorySeparatorChar + newFileName + ext; + var newFile = folder + Path.DirectorySeparatorChar + newFileName + ext; - //Do the rename - _diskProvider.RenameFile(episodeFile.Path, newFile); + //Do the rename + _diskProvider.RenameFile(episodeFile.Path, newFile); - //Update the filename in the DB - episodeFile.Path = newFile; - Update(episodeFile); + //Update the filename in the DB + episodeFile.Path = newFile; + Update(episodeFile); - notification.CurrentMessage = String.Format("Finished Renaming '{0}'", newFile); - } - catch (Exception e) - { - notification.CurrentMessage = String.Format("Failed to Rename '{0}'", episodeFile.Path); - Logger.ErrorException("An error has occurred while renaming episode: " + episodeFile.Path, e); - throw; - } return true; } } From cfcb9e61d7dde5976224ec548332584d9beee1de Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 19:33:43 -0700 Subject: [PATCH 2/8] Cleaned up import file a bit --- NzbDrone.Core.Test/MediaFileProviderTests.cs | 27 +-- NzbDrone.Core/Providers/MediaFileProvider.cs | 169 ++++++++++--------- 2 files changed, 91 insertions(+), 105 deletions(-) diff --git a/NzbDrone.Core.Test/MediaFileProviderTests.cs b/NzbDrone.Core.Test/MediaFileProviderTests.cs index e99763e0b..1e734652a 100644 --- a/NzbDrone.Core.Test/MediaFileProviderTests.cs +++ b/NzbDrone.Core.Test/MediaFileProviderTests.cs @@ -129,19 +129,16 @@ namespace NzbDrone.Core.Test } [Test] - [Description("Verifies that a new file imported properly")] - public void import_existing_season_file() + public void import_existing_season_file_should_skip() { //Arrange ///////////////////////////////////////// //Constants const string fileName = @"WEEDS.S03E01.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi"; - const int size = 12345; //Fakes var fakeSeries = Builder.CreateNew().Build(); - var fakeEpisodeFile = Builder.CreateNew().With(s => s.SeriesId = fakeSeries.SeriesId).Build(); //Mocks var mocker = new AutoMoqer(); @@ -149,10 +146,6 @@ namespace NzbDrone.Core.Test mocker.GetMock(MockBehavior.Strict) .Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(true).Verifiable(); - mocker.GetMock() - .Setup(e => e.GetSize(fileName)).Returns(size).Verifiable(); - - //Act var result = mocker.Resolve().ImportFile(fakeSeries, fileName); @@ -232,33 +225,23 @@ namespace NzbDrone.Core.Test } [Test] - [Description("Verifies that an existing file will skip import")] public void import_existing_file() { - //Arrange - ///////////////////////////////////////// - - //Constants const string fileName = "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi"; - //Fakes var fakeSeries = Builder.CreateNew().Build(); - //Mocks var mocker = new AutoMoqer(); mocker.GetMock(MockBehavior.Strict) .Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(true).Verifiable(); mocker.GetMock(MockBehavior.Strict); - mocker.GetMock() - .Setup(e => e.GetSize(fileName)).Returns(500000).Verifiable(); - //Act var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert - Assert.IsNull(result); + result.Should().BeNull(); mocker.GetMock().Verify(r => r.Insert(result), Times.Never()); mocker.VerifyAllMocks(); } @@ -296,7 +279,7 @@ namespace NzbDrone.Core.Test //Assert mocker.VerifyAllMocks(); - Assert.IsNull(result); + result.Should().BeNull(); mocker.GetMock().Verify(r => r.Insert(result), Times.Never()); ExceptionVerification.ExcpectedWarns(1); } @@ -311,7 +294,7 @@ namespace NzbDrone.Core.Test mocker.GetMock() .Setup(c => c.GetEpisodeBySeries(It.IsAny())) - .Returns(new List{new Episode()}); + .Returns(new List { new Episode() }); mocker.Resolve().Scan(new Series()); @@ -319,7 +302,7 @@ namespace NzbDrone.Core.Test } - + [Test] public void get_series_files() { diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index 34c6d81fe..4e003eeb7 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -69,98 +69,90 @@ namespace NzbDrone.Core.Providers { Logger.Trace("Importing file to database [{0}]", filePath); - try + if (_database.Exists("Path =@0", Parser.NormalizePath(filePath))) { - var size = _diskProvider.GetSize(filePath); + Logger.Trace("[{0}] already exists in the database. skipping.", filePath); + return null; + } + + var size = _diskProvider.GetSize(filePath); - //If Size is less than 50MB and contains sample. Check for Size to ensure its not an episode with sample in the title - if (size < 40000000 && filePath.ToLower().Contains("sample")) - { - Logger.Trace("[{0}] appears to be a sample. skipping.", filePath); - return null; - } + //If Size is less than 50MB and contains sample. Check for Size to ensure its not an episode with sample in the title + if (size < 40000000 && filePath.ToLower().Contains("sample")) + { + Logger.Trace("[{0}] appears to be a sample. skipping.", filePath); + return null; + } - //Check to see if file already exists in the database - if (!_database.Exists("Path =@0", Parser.NormalizePath(filePath))) - { - var parseResult = Parser.ParseEpisodeInfo(filePath); + var parseResult = Parser.ParseEpisodeInfo(filePath); - if (parseResult == null) - return null; + if (parseResult == null) + return null; - parseResult.CleanTitle = series.Title;//replaces the nasty path as title to help with logging + parseResult.CleanTitle = series.Title;//replaces the nasty path as title to help with logging - //Stores the list of episodes to add to the EpisodeFile - var episodes = new List(); + //Stores the list of episodes to add to the EpisodeFile + var episodes = new List(); + + //Check for daily shows + if (parseResult.EpisodeNumbers == null) + { + var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.AirDate.Date); + + if (episode != null) + { + episodes.Add(episode); + } + else + { + Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); + } + } + else + { + foreach (var episodeNumber in parseResult.EpisodeNumbers) + { + var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.SeasonNumber, + episodeNumber); - //Check for daily shows - if (parseResult.EpisodeNumbers == null) + if (episode != null) { - var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.AirDate.Date); - - if (episode != null) - { - episodes.Add(episode); - } - else - { - Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); - } + episodes.Add(episode); } else { - foreach (var episodeNumber in parseResult.EpisodeNumbers) - { - var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.SeasonNumber, - episodeNumber); - - if (episode != null) - { - episodes.Add(episode); - } - else - { - Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); - } - } + Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); } - - //Return null if no Episodes exist in the DB for the parsed episodes from file - if (episodes.Count <= 0) - return null; - - var episodeFile = new EpisodeFile(); - episodeFile.DateAdded = DateTime.Now; - episodeFile.SeriesId = series.SeriesId; - episodeFile.Path = Parser.NormalizePath(filePath); - episodeFile.Size = size; - episodeFile.Quality = parseResult.Quality.QualityType; - episodeFile.Proper = parseResult.Quality.Proper; - episodeFile.SeasonNumber = parseResult.SeasonNumber; - var fileId = Convert.ToInt32(_database.Insert(episodeFile)); - - //This is for logging + updating the episodes that are linked to this EpisodeFile - string episodeList = String.Empty; - foreach (var ep in episodes) - { - ep.EpisodeFileId = fileId; - _episodeProvider.UpdateEpisode(ep); - episodeList += String.Format(", {0}", ep.EpisodeId).Trim(' ', ','); - } - Logger.Trace("File {0}:{1} attached to episode(s): '{2}'", episodeFile.EpisodeFileId, filePath, - episodeList); - - return episodeFile; } - - Logger.Trace("[{0}] already exists in the database. skipping.", filePath); } - catch (Exception ex) + + //Return null if no Episodes exist in the DB for the parsed episodes from file + if (episodes.Count <= 0) + return null; + + var episodeFile = new EpisodeFile(); + episodeFile.DateAdded = DateTime.Now; + episodeFile.SeriesId = series.SeriesId; + episodeFile.Path = Parser.NormalizePath(filePath); + episodeFile.Size = size; + episodeFile.Quality = parseResult.Quality.QualityType; + episodeFile.Proper = parseResult.Quality.Proper; + episodeFile.SeasonNumber = parseResult.SeasonNumber; + var fileId = Convert.ToInt32(_database.Insert(episodeFile)); + + //This is for logging + updating the episodes that are linked to this EpisodeFile + string episodeList = String.Empty; + foreach (var ep in episodes) { - Logger.ErrorException("An error has occurred while importing file " + filePath, ex); - throw; + ep.EpisodeFileId = fileId; + _episodeProvider.UpdateEpisode(ep); + episodeList += String.Format(", {0}", ep.EpisodeId).Trim(' ', ','); } - return null; + Logger.Trace("File {0}:{1} attached to episode(s): '{2}'", episodeFile.EpisodeFileId, filePath, + episodeList); + + return episodeFile; + } /// @@ -311,7 +303,7 @@ namespace NzbDrone.Core.Providers return result; } - public virtual string GetNewFilename(IList episodes, string seriesName, QualityTypes quality) + public virtual string GetNewFilename(IList episodes, string seriesTitle, QualityTypes quality) { var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SeparatorStyle); var numberStyle = EpisodeSortingHelper.GetNumberStyle(_configProvider.NumberStyle); @@ -322,7 +314,7 @@ namespace NzbDrone.Core.Providers if (_configProvider.SeriesName) { - result += seriesName + separatorStyle.Pattern; + result += seriesTitle + separatorStyle.Pattern; } result += numberStyle.Pattern.Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); @@ -369,25 +361,36 @@ namespace NzbDrone.Core.Providers return result.Trim(); } + public virtual FileInfo CalculateFilePath(Series series, int seasonNumber, string fileName, string extention) + { + var path = series.Path; + if (series.SeasonFolder) + { + path = Path.Combine(path, "Season " + seasonNumber); + } + + path = Path.Combine(path, fileName + extention); + + return new FileInfo(path); + } + public virtual bool RenameEpisodeFile(EpisodeFile episodeFile) { if (episodeFile == null) throw new ArgumentNullException("episodeFile"); var series = _seriesProvider.GetSeries(episodeFile.SeriesId); - var folder = new FileInfo(episodeFile.Path).DirectoryName; var ext = _diskProvider.GetExtension(episodeFile.Path); var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId); - var newFileName = GetNewFilename(episodes, series.Title, episodeFile.Quality); - var newFile = folder + Path.DirectorySeparatorChar + newFileName + ext; + var newFile = CalculateFilePath(series, episodes.First().SeasonNumber, newFileName, ext); //Do the rename - _diskProvider.RenameFile(episodeFile.Path, newFile); + _diskProvider.RenameFile(episodeFile.Path, newFile.FullName); //Update the filename in the DB - episodeFile.Path = newFile; + episodeFile.Path = newFile.FullName; Update(episodeFile); From 63bd972af9685272d9181f6fac1772f456a564bd Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 20:04:08 -0700 Subject: [PATCH 3/8] Refactored MediaFileProvider --- NzbDrone.Core.Test/DiskScanJobTest.cs | 16 +- NzbDrone.Core.Test/MediaFileProviderTests.cs | 135 +----- ...MediaFileProvider_ImportNewDownloadTest.cs | 444 ------------------ NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 - NzbDrone.Core/NzbDrone.Core.csproj | 1 + NzbDrone.Core/Providers/DiskScanProvider.cs | 413 +++++----------- NzbDrone.Core/Providers/Jobs/DiskScanJob.cs | 8 +- .../Providers/Jobs/PostDownloadScanJob.cs | 8 +- .../Providers/Jobs/RenameEpisodeJob.cs | 16 +- NzbDrone.Core/Providers/MediaFileProvider.cs | 267 +---------- 10 files changed, 154 insertions(+), 1155 deletions(-) delete mode 100644 NzbDrone.Core.Test/MediaFileProvider_ImportNewDownloadTest.cs diff --git a/NzbDrone.Core.Test/DiskScanJobTest.cs b/NzbDrone.Core.Test/DiskScanJobTest.cs index f653e9624..4c6636827 100644 --- a/NzbDrone.Core.Test/DiskScanJobTest.cs +++ b/NzbDrone.Core.Test/DiskScanJobTest.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test .Setup(p => p.GetSeries(series.SeriesId)) .Returns(series); - mocker.GetMock() + mocker.GetMock() .Setup(p => p.Scan(series)) .Returns(new List()); @@ -58,11 +58,11 @@ namespace NzbDrone.Core.Test .Setup(p => p.GetAllSeries()) .Returns(series); - mocker.GetMock() + mocker.GetMock() .Setup(s => s.Scan(series[0])) .Returns(new List()); - mocker.GetMock() + mocker.GetMock() .Setup(s => s.Scan(series[1])) .Returns(new List()); @@ -86,11 +86,11 @@ namespace NzbDrone.Core.Test .Setup(p => p.GetAllSeries()) .Returns(series); - mocker.GetMock() + mocker.GetMock() .Setup(s => s.Scan(series[0])) .Throws(new InvalidOperationException("Bad Job")); - mocker.GetMock() + mocker.GetMock() .Setup(s => s.Scan(series[1])) .Throws(new InvalidOperationException("Bad Job")); @@ -115,11 +115,11 @@ namespace NzbDrone.Core.Test .Setup(p => p.GetAllSeries()) .Returns(series); - mocker.GetMock() + mocker.GetMock() .Setup(s => s.Scan(series[0])) .Returns(new List()); - mocker.GetMock() + mocker.GetMock() .Setup(s => s.Scan(series[1])) .Returns(new List()); @@ -128,7 +128,7 @@ namespace NzbDrone.Core.Test mocker.VerifyAllMocks(); - mocker.GetMock().Verify(s => s.Scan(It.IsAny()), Times.Exactly(2)); + mocker.GetMock().Verify(s => s.Scan(It.IsAny()), Times.Exactly(2)); } } diff --git a/NzbDrone.Core.Test/MediaFileProviderTests.cs b/NzbDrone.Core.Test/MediaFileProviderTests.cs index 1e734652a..8f6ad61c1 100644 --- a/NzbDrone.Core.Test/MediaFileProviderTests.cs +++ b/NzbDrone.Core.Test/MediaFileProviderTests.cs @@ -60,7 +60,7 @@ namespace NzbDrone.Core.Test .Setup(e => e.GetEpisode(fakeSeries.SeriesId, seasonNumber, episodeNumner)).Returns(fakeEpisode); //Act - var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert Assert.IsNotNull(result); @@ -107,7 +107,7 @@ namespace NzbDrone.Core.Test .Setup(e => e.GetSize(fileName)).Returns(size).Verifiable(); //Act - var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert Assert.IsNotNull(result); @@ -147,7 +147,7 @@ namespace NzbDrone.Core.Test .Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(true).Verifiable(); //Act - var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert mocker.VerifyAllMocks(); @@ -178,7 +178,7 @@ namespace NzbDrone.Core.Test .Setup(e => e.GetSize(fileName)).Returns(size).Verifiable(); //Act - var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert mocker.VerifyAllMocks(); @@ -218,7 +218,7 @@ namespace NzbDrone.Core.Test //Act - var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert Assert.IsNull(result); @@ -238,7 +238,7 @@ namespace NzbDrone.Core.Test mocker.GetMock(MockBehavior.Strict); //Act - var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert result.Should().BeNull(); @@ -275,7 +275,7 @@ namespace NzbDrone.Core.Test //Act - var result = mocker.Resolve().ImportFile(fakeSeries, fileName); + var result = mocker.Resolve().ImportFile(fakeSeries, fileName); //Assert mocker.VerifyAllMocks(); @@ -296,7 +296,7 @@ namespace NzbDrone.Core.Test .Setup(c => c.GetEpisodeBySeries(It.IsAny())) .Returns(new List { new Episode() }); - mocker.Resolve().Scan(new Series()); + mocker.Resolve().Scan(new Series()); mocker.VerifyAllMocks(); @@ -330,123 +330,6 @@ namespace NzbDrone.Core.Test result.Should().HaveSameCount(firstSeriesFiles); } - [Test] - [Description("Verifies that a new download will import successfully")] - public void import_new_download_success() - { - //Fakes - var fakeSeries = Builder.CreateNew() - .With(s => s.Title = "30 Rock") - .With(s => s.Path = @"C:\Test\TV\30 Rock") - .Build(); - - var fakeEpisode = Builder.CreateNew() - .With(e => e.SeriesId = fakeSeries.SeriesId) - .With(e => e.EpisodeFileId = 0) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 5) - .Build(); - - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".avi"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { fakeEpisode }); - episodeProvider.Setup(e => e.GetEpisode(fakeSeries.SeriesId, 1, 5)).Returns(fakeEpisode); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(MockBehavior.Strict); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1).Verifiable(); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Episode Title", fakeSeries); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - - [Test] - [Description("Verifies that a new download will import successfully, deletes previous episode")] - public void import_new_download_success_delete_equal_quality() - { - //Fakes - var fakeSeries = Builder.CreateNew() - .With(s => s.Title = "30 Rock") - .With(s => s.Path = @"C:\Test\TV\30 Rock") - .Build(); - - var fakeEpisode = Builder.CreateNew() - .With(e => e.SeriesId = fakeSeries.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 5) - .With(e => e.EpisodeFileId = 1) - .Build(); - - var fakeEpisodeFile = Builder.CreateNew() - .With(e => e.SeriesId = fakeSeries.SeriesId) - .With(e => e.EpisodeFileId = 1) - .With(e => e.Quality = QualityTypes.SDTV) - .With(e => e.Episodes = new List { fakeEpisode }) - .Build(); - - fakeEpisode.EpisodeFile = fakeEpisodeFile; - - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".avi"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { fakeEpisode }); - episodeProvider.Setup(e => e.GetEpisode(fakeSeries.SeriesId, 1, 5)).Returns(fakeEpisode); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(MockBehavior.Strict); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1); - database.Setup(r => r.Delete(It.IsAny())).Returns(1); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Episode Title", fakeSeries); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - - [Test] public void Scan_series_should_skip_series_with_no_episodes() { @@ -459,7 +342,7 @@ namespace NzbDrone.Core.Test .With(s => s.SeriesId = 12).Build(); //Act - mocker.Resolve().Scan(series); + mocker.Resolve().Scan(series); //Assert mocker.VerifyAllMocks(); diff --git a/NzbDrone.Core.Test/MediaFileProvider_ImportNewDownloadTest.cs b/NzbDrone.Core.Test/MediaFileProvider_ImportNewDownloadTest.cs deleted file mode 100644 index c3a46ab16..000000000 --- a/NzbDrone.Core.Test/MediaFileProvider_ImportNewDownloadTest.cs +++ /dev/null @@ -1,444 +0,0 @@ -// ReSharper disable RedundantUsingDirective -using System.Collections.Generic; -using System.IO; -using AutoMoq; -using FizzWare.NBuilder; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Model; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Providers.Core; -using NzbDrone.Core.Repository; -using NzbDrone.Core.Repository.Quality; -using NzbDrone.Core.Test.Framework; -using PetaPoco; - -namespace NzbDrone.Core.Test -{ - [TestFixture] - // ReSharper disable InconsistentNaming - public class MediaFileProvider_ImportNewDownloadTest : TestBase - { - private Episode episode; - private Episode episode2; - private EpisodeFile episodeFile; - private EpisodeFile episodeFile2; - private Series series; - - [SetUp] - public new void Setup() - { - series = Builder.CreateNew() - .With(s => s.Title = "30 Rock") - .With(s => s.Path = @"C:\Test\TV\30 Rock") - .Build(); - - episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 5) - .With(e => e.EpisodeFileId = 1) - .With(e => e.Title = "Episode One Title") - .Build(); - - episode2 = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 6) - .With(e => e.EpisodeFileId = 1) - .With(e => e.Title = "Episode Two Title") - .Build(); - - episodeFile = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.EpisodeFileId = 1) - .With(e => e.Quality = QualityTypes.SDTV) - .With(e => e.Episodes = new List { episode }) - .Build(); - - episodeFile2 = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.EpisodeFileId = 1) - .With(e => e.Quality = QualityTypes.SDTV) - .With(e => e.Episodes = new List { episode }) - .Build(); - - episode.EpisodeFile = episodeFile; - - base.Setup(); - } - - [Test] - [Description("Verifies that a new download will import successfully")] - public void import_new_download_imported() - { - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".avi"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List {episode}); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 5)).Returns(episode); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - - [Test] - [Description("Verifies that a new download will import successfully, deletes previous episode")] - public void import_new_download_imported_delete_equal_quality() - { - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".avi"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { episode }); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 5)).Returns(episode); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - - [Test] - [Description("Verifies that a new download will not import successfully, because existing episode is better")] - public void import_new_download_not_imported_greater_quality() - { - //Alternate Setups - episodeFile.Quality = QualityTypes.DVD; - - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".avi"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { episode }); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(0, result.Count); - } - - [Test] - [Description("Verifies that a new download will not import successfully, because of invalid episode in new file")] - public void import_new_download_not_imported_non_existant_episode() - { - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List()); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(0, result.Count); - ExceptionVerification.ExcpectedErrors(1); - } - - [Test] - [Description("Verifies that a new download will import successfully, deletes previous episode")] - public void import_new_download_imported_delete_lesser_quality_multi_episodes() - { - //Alternate Setup - episodeFile.Episodes.Add(episode2); - episode2.EpisodeFile = episodeFile; - - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05x06 - Episode Title\30.Rock.S01E05E06.Gibberish.x264.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".mkv"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { episode, episode2 }); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 5)).Returns(episode); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 6)).Returns(episode2); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05x06 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - - [Test] - [Description("Verifies that a new download will import successfully, deletes previous episode")] - public void import_new_download_imported_delete_lesser_quality_multi_episode_files() - { - //Alternate Setup - episodeFile2.Episodes.Add(episode2); - episode2.EpisodeFile = episodeFile2; - - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05x06 - Episode Title\30.Rock.S01E05E06.Gibberish.x264.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".mkv"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { episode, episode2 }); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 5)).Returns(episode); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 6)).Returns(episode2); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05x06 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - - [Test] - [Description("Verifies that a new download will not import successfully, previous episode is better quality")] - public void import_new_download_not_imported_multi_episode_files() - { - //Alternate Setup - episodeFile2.Episodes.Add(episode2); - episode2.EpisodeFile = episodeFile2; - episodeFile2.Quality = QualityTypes.Bluray720p; - - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05x06 - Episode Title\30.Rock.S01E05E06.Gibberish.x264.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".mkv"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { episode, episode2 }); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05x06 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(0, result.Count); - } - - [Test] - [Description("Verifies that a new download will not import successfully, episode is sample under 40MB")] - public void import_new_download_not_imported_episode_sample_under_40MB() - { - //Alternate Setup - episodeFile2.Episodes.Add(episode2); - episode2.EpisodeFile = episodeFile2; - episodeFile2.Quality = QualityTypes.Bluray720p; - - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05x06 - Episode Title\30.Rock.S01E05.Gibberish.x264-sample.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(30000000); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05x06 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(0, result.Count); - } - - [Test] - [Description("Verifies that a new download will import successfully, even though the episode title contains Sample")] - public void import_new_download_imported_contains_sample_over_40MB() - { - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Fourty.Samples.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(90000000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".avi"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { episode }); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 5)).Returns(episode); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Fourty Samples", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - - [Test] - [Description("Verifies that a new download will import successfully, even though the efile size is under 40MB")] - public void import_new_download_imported_under_40MB() - { - //Mocks - var mocker = new AutoMoqer(); - - var diskProvider = mocker.GetMock(); - diskProvider.Setup(d => d.GetFiles(It.IsAny(), "*.*", SearchOption.AllDirectories)).Returns(new string[] { @"C:\Test\30 Rock - 1x05 - Episode Title\30.Rock.S01E05.Gibberish.XviD.avi" }); - diskProvider.Setup(d => d.GetSize(It.IsAny())).Returns(30000000); - diskProvider.Setup(d => d.CreateDirectory(It.IsAny())).Returns("ok"); - diskProvider.Setup(d => d.RenameFile(It.IsAny(), It.IsAny())); - diskProvider.Setup(d => d.GetExtension(It.IsAny())).Returns(".avi"); - - var episodeProvider = mocker.GetMock(); - episodeProvider.Setup(e => e.GetEpisodes(It.IsAny())).Returns(new List { episode }); - episodeProvider.Setup(e => e.GetEpisode(series.SeriesId, 1, 5)).Returns(episode); - - var configProvider = mocker.GetMock(); - configProvider.SetupGet(c => c.UseSeasonFolder).Returns(true); - configProvider.SetupGet(c => c.SeasonFolderFormat).Returns(@"Season %0s"); - configProvider.SetupGet(c => c.SeriesName).Returns(true); - configProvider.SetupGet(c => c.EpisodeName).Returns(true); - configProvider.SetupGet(c => c.AppendQuality).Returns(true); - configProvider.SetupGet(c => c.SeparatorStyle).Returns(0); - configProvider.SetupGet(c => c.NumberStyle).Returns(2); - configProvider.SetupGet(c => c.ReplaceSpaces).Returns(false); - - var database = mocker.GetMock(); - database.Setup(r => r.Exists(It.IsAny(), It.IsAny())).Returns(false).Verifiable(); - database.Setup(r => r.Insert(It.IsAny())).Returns(1); - - //Act - var result = mocker.Resolve().ImportNewFiles(@"C:\Test\30 Rock - 1x05 - Episode Title", series); - - //Assert - mocker.VerifyAllMocks(); - Assert.AreEqual(1, result.Count); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index fd96cd025..ff97e5004 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -87,7 +87,6 @@ - diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index d1daa6c0d..785b24271 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -180,6 +180,7 @@ + diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 0483d14d2..996be81b7 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -14,41 +14,50 @@ using PetaPoco; namespace NzbDrone.Core.Providers { - class DiskScanProvider + public class DiskScanProvider { - + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly string[] MediaExtentions = new[] { ".mkv", ".avi", ".wmv", ".mp4" }; private readonly DiskProvider _diskProvider; private readonly EpisodeProvider _episodeProvider; private readonly SeriesProvider _seriesProvider; - private readonly ConfigProvider _configProvider; + private readonly MediaFileProvider _mediaFileProvider; private readonly IDatabase _database; [Inject] public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider, - SeriesProvider seriesProvider, ConfigProvider configProvider, + SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider, IDatabase database) { _diskProvider = diskProvider; _episodeProvider = episodeProvider; _seriesProvider = seriesProvider; - _configProvider = configProvider; + _mediaFileProvider = mediaFileProvider; _database = database; } public DiskScanProvider() { - - } + } /// /// Scans the specified series folder for media files /// /// The series to be scanned public virtual List Scan(Series series) + { + return Scan(series, series.Path); + } + + /// + /// Scans the specified series folder for media files + /// + /// The series to be scanned + /// Path to scan + public virtual List Scan(Series series, string path) { if (_episodeProvider.GetEpisodeBySeries(series.SeriesId).Count == 0) { @@ -56,7 +65,7 @@ namespace NzbDrone.Core.Providers return new List(); } - var mediaFileList = GetVideoFiles(series.Path); + var mediaFileList = GetVideoFiles(path); var fileList = new List(); foreach (var filePath in mediaFileList) @@ -72,104 +81,125 @@ namespace NzbDrone.Core.Providers return fileList; } + public virtual EpisodeFile ImportFile(Series series, string filePath) { Logger.Trace("Importing file to database [{0}]", filePath); - try + if (_database.Exists("Path =@0", Parser.NormalizePath(filePath))) { - var size = _diskProvider.GetSize(filePath); + Logger.Trace("[{0}] already exists in the database. skipping.", filePath); + return null; + } - //If Size is less than 50MB and contains sample. Check for Size to ensure its not an episode with sample in the title - if (size < 40000000 && filePath.ToLower().Contains("sample")) - { - Logger.Trace("[{0}] appears to be a sample. skipping.", filePath); - return null; - } + var size = _diskProvider.GetSize(filePath); - //Check to see if file already exists in the database - if (!_database.Exists("Path =@0", Parser.NormalizePath(filePath))) - { - var parseResult = Parser.ParseEpisodeInfo(filePath); + //If Size is less than 50MB and contains sample. Check for Size to ensure its not an episode with sample in the title + if (size < 40000000 && filePath.ToLower().Contains("sample")) + { + Logger.Trace("[{0}] appears to be a sample. skipping.", filePath); + return null; + } + + var parseResult = Parser.ParseEpisodeInfo(filePath); - if (parseResult == null) - return null; + if (parseResult == null) + return null; - parseResult.CleanTitle = series.Title;//replaces the nasty path as title to help with logging + parseResult.CleanTitle = series.Title;//replaces the nasty path as title to help with logging - //Stores the list of episodes to add to the EpisodeFile - var episodes = new List(); + //Stores the list of episodes to add to the EpisodeFile + var episodes = new List(); - //Check for daily shows - if (parseResult.EpisodeNumbers == null) + //Check for daily shows + if (parseResult.EpisodeNumbers == null) + { + var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.AirDate.Date); + + if (episode != null) + { + episodes.Add(episode); + } + else + { + Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); + } + } + else + { + foreach (var episodeNumber in parseResult.EpisodeNumbers) + { + var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.SeasonNumber, + episodeNumber); + + if (episode != null) { - var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.AirDate.Date); - - if (episode != null) - { - episodes.Add(episode); - } - else - { - Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); - } + episodes.Add(episode); } else { - foreach (var episodeNumber in parseResult.EpisodeNumbers) - { - var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.SeasonNumber, - episodeNumber); - - if (episode != null) - { - episodes.Add(episode); - } - else - { - Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); - } - } - } - - //Return null if no Episodes exist in the DB for the parsed episodes from file - if (episodes.Count <= 0) - return null; - - var episodeFile = new EpisodeFile(); - episodeFile.DateAdded = DateTime.Now; - episodeFile.SeriesId = series.SeriesId; - episodeFile.Path = Parser.NormalizePath(filePath); - episodeFile.Size = size; - episodeFile.Quality = parseResult.Quality.QualityType; - episodeFile.Proper = parseResult.Quality.Proper; - episodeFile.SeasonNumber = parseResult.SeasonNumber; - var fileId = Convert.ToInt32(_database.Insert(episodeFile)); - - //This is for logging + updating the episodes that are linked to this EpisodeFile - string episodeList = String.Empty; - foreach (var ep in episodes) - { - ep.EpisodeFileId = fileId; - _episodeProvider.UpdateEpisode(ep); - episodeList += String.Format(", {0}", ep.EpisodeId).Trim(' ', ','); + Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); } - Logger.Trace("File {0}:{1} attached to episode(s): '{2}'", episodeFile.EpisodeFileId, filePath, - episodeList); - - return episodeFile; } - - Logger.Trace("[{0}] already exists in the database. skipping.", filePath); } - catch (Exception ex) + + //Return null if no Episodes exist in the DB for the parsed episodes from file + if (episodes.Count <= 0) + return null; + + var episodeFile = new EpisodeFile(); + episodeFile.DateAdded = DateTime.Now; + episodeFile.SeriesId = series.SeriesId; + episodeFile.Path = Parser.NormalizePath(filePath); + episodeFile.Size = size; + episodeFile.Quality = parseResult.Quality.QualityType; + episodeFile.Proper = parseResult.Quality.Proper; + episodeFile.SeasonNumber = parseResult.SeasonNumber; + var fileId = Convert.ToInt32(_database.Insert(episodeFile)); + + //This is for logging + updating the episodes that are linked to this EpisodeFile + string episodeList = String.Empty; + foreach (var ep in episodes) { - Logger.ErrorException("An error has occurred while importing file " + filePath, ex); - throw; + ep.EpisodeFileId = fileId; + _episodeProvider.UpdateEpisode(ep); + episodeList += String.Format(", {0}", ep.EpisodeId).Trim(' ', ','); } - return null; + Logger.Trace("File {0}:{1} attached to episode(s): '{2}'", episodeFile.EpisodeFileId, filePath, + episodeList); + + return episodeFile; + } + + + + public virtual bool RenameEpisodeFile(EpisodeFile episodeFile) + { + if (episodeFile == null) + throw new ArgumentNullException("episodeFile"); + + var series = _seriesProvider.GetSeries(episodeFile.SeriesId); + var ext = _diskProvider.GetExtension(episodeFile.Path); + var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId); + var newFileName = _mediaFileProvider.GetNewFilename(episodes, series.Title, episodeFile.Quality); + + var newFile = _mediaFileProvider.CalculateFilePath(series, episodes.First().SeasonNumber, newFileName, ext); + + //Do the rename + _diskProvider.RenameFile(episodeFile.Path, newFile.FullName); + + //Update the filename in the DB + episodeFile.Path = newFile.FullName; + _mediaFileProvider.Update(episodeFile); + + + return true; + } + + + /// /// Removes files that no longer exist from the database /// @@ -197,7 +227,6 @@ namespace NzbDrone.Core.Providers } - private List GetVideoFiles(string path) { Logger.Debug("Scanning '{0}' for episodes", path); @@ -210,219 +239,5 @@ namespace NzbDrone.Core.Providers return mediaFileList; } - public virtual List ImportNewFiles(string path, Series series) - { - var result = new List(); - - //Get all the files except those that are considered samples - var files = GetVideoFiles(path).Where(f => _diskProvider.GetSize(f) > 40000000 || !f.ToLower().Contains("sample")).ToList(); - - foreach (var file in files) - { - try - { - //Parse the filename - var parseResult = Parser.ParseEpisodeInfo(Path.GetFileName(file)); - parseResult.Series = series; - parseResult.Episodes = _episodeProvider.GetEpisodes(parseResult); - - if (parseResult.Episodes.Count == 0) - { - Logger.Error("File '{0}' contains invalid episode information, skipping import", file); - continue; - } - - var ext = _diskProvider.GetExtension(file); - var filename = GetNewFilename(parseResult.Episodes, series.Title, parseResult.Quality.QualityType) + ext; - var folder = series.Path + Path.DirectorySeparatorChar; - if (_configProvider.UseSeasonFolder) - folder += _configProvider.SeasonFolderFormat - .Replace("%0s", parseResult.SeasonNumber.ToString("00")) - .Replace("%s", parseResult.SeasonNumber.ToString()) - + Path.DirectorySeparatorChar; - - _diskProvider.CreateDirectory(folder); - - //Get a list of episodeFiles that we need to delete and cleanup - var episodeFilesToClean = new List(); - - foreach (var episode in parseResult.Episodes) - { - if (episode.EpisodeFileId > 0) - episodeFilesToClean.Add(episode.EpisodeFile); - } - - if (episodeFilesToClean.Count != episodeFilesToClean.Where(e => parseResult.Quality.QualityType >= e.Quality).Count()) - { - Logger.Debug("Episode isn't an upgrade for all episodes in file: [{0}]. Skipping.", file); - continue; - } - - //Delete the files and then cleanup! - foreach (var e in episodeFilesToClean) - { - if (_diskProvider.FileExists(e.Path)) - _diskProvider.DeleteFile(e.Path); - } - - CleanUp(episodeFilesToClean); - - //Move the file - _diskProvider.RenameFile(file, folder + filename); - - //Import into DB - result.Add(ImportFile(series, folder + filename)); - } - - catch (Exception ex) - { - Logger.WarnException("Error importing new download: " + file, ex); - } - } - - //If we have imported all the non-sample files, delete the folder, requires a minimum of 1 file to be imported. - if (files.Count() > 0 && files.Count() == result.Count) - { - Logger.Debug("All non-sample files have been processed, deleting folder: {0}", path); - _diskProvider.DeleteFolder(path, true); - } - - return result; - } - - public virtual string GetNewFilename(IList episodes, string seriesName, QualityTypes quality) - { - var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SeparatorStyle); - var numberStyle = EpisodeSortingHelper.GetNumberStyle(_configProvider.NumberStyle); - var useSeriesName = _configProvider.SeriesName; - var useEpisodeName = _configProvider.EpisodeName; - var replaceSpaces = _configProvider.ReplaceSpaces; - var appendQuality = _configProvider.AppendQuality; - - var title = String.Empty; - - if (episodes.Count == 1) - { - if (useSeriesName) - { - title += seriesName; - title += separatorStyle.Pattern; - } - - title += numberStyle.Pattern.Replace("%s", String.Format("{0}", episodes[0].SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episodes[0].SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); - - if (useEpisodeName) - { - title += separatorStyle.Pattern; - title += episodes[0].Title; - } - - if (appendQuality) - title += String.Format(" [{0}]", quality); - - if (replaceSpaces) - title = title.Replace(' ', '.'); - - Logger.Debug("New File Name is: {0}", title); - return title; - } - - var multiEpisodeStyle = EpisodeSortingHelper.GetMultiEpisodeStyle(_configProvider.MultiEpisodeStyle); - - if (useSeriesName) - { - title += seriesName; - title += separatorStyle.Pattern; - } - - title += numberStyle.Pattern.Replace("%s", String.Format("{0}", episodes[0].SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episodes[0].SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episodes[0].EpisodeNumber)); - - var numbers = String.Empty; - var episodeNames = episodes[0].Title; - - for (int i = 1; i < episodes.Count; i++) - { - var episode = episodes[i]; - - if (multiEpisodeStyle.Name == "Duplicate") - { - numbers += separatorStyle.Pattern + numberStyle.Pattern.Replace("%s", String.Format("{0}", episode.SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episode.SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber)); - } - else - { - numbers += multiEpisodeStyle.Pattern.Replace("%s", String.Format("{0}", episode.SeasonNumber)) - .Replace("%0s", String.Format("{0:00}", episode.SeasonNumber)) - .Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber)) - .Replace("%x", numberStyle.EpisodeSeparator) - .Replace("%p", separatorStyle.Pattern); - } - - episodeNames += String.Format(" + {0}", episode.Title); - } - - title += numbers; - - if (useEpisodeName) - { - episodeNames = episodeNames.TrimEnd(' ', '+'); - - title += separatorStyle.Pattern; - title += episodeNames; - } - - if (appendQuality) - title += String.Format(" [{0}]", quality); - - if (replaceSpaces) - title = title.Replace(' ', '.'); - - Logger.Debug("New File Name is: {0}", title); - return title; - } - - public virtual bool RenameEpisodeFile(int episodeFileId, ProgressNotification notification) - { - var episodeFile = GetEpisodeFile(episodeFileId); - - if (episodeFile == null) - return false; - - try - { - notification.CurrentMessage = String.Format("Renaming '{0}'", episodeFile.Path); - - var series = _seriesProvider.GetSeries(episodeFile.SeriesId); - var folder = new FileInfo(episodeFile.Path).DirectoryName; - var episodes = _episodeProvider.EpisodesByFileId(episodeFileId); - var ext = _diskProvider.GetExtension(episodeFile.Path); - - var newFileName = GetNewFilename(episodes, series.Title, episodeFile.Quality); - - var newFile = folder + Path.DirectorySeparatorChar + newFileName + ext; - - //Do the rename - _diskProvider.RenameFile(episodeFile.Path, newFile); - - //Update the filename in the DB - episodeFile.Path = newFile; - Update(episodeFile); - - notification.CurrentMessage = String.Format("Finished Renaming '{0}'", newFile); - } - - catch (Exception e) - { - notification.CurrentMessage = String.Format("Failed to Rename '{0}'", episodeFile.Path); - Logger.ErrorException("An error has occurred while renaming episode: " + episodeFile.Path, e); - throw; - } - return true; - } } } diff --git a/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs b/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs index 58bdcf0c2..fc1ea6cf1 100644 --- a/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs +++ b/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs @@ -11,14 +11,14 @@ namespace NzbDrone.Core.Providers.Jobs public class DiskScanJob : IJob { private readonly SeriesProvider _seriesProvider; - private readonly MediaFileProvider _mediaFileProvider; + private readonly DiskScanProvider _diskScanProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] - public DiskScanJob(SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider) + public DiskScanJob(SeriesProvider seriesProvider, DiskScanProvider diskScanProvider) { _seriesProvider = seriesProvider; - _mediaFileProvider = mediaFileProvider; + _diskScanProvider = diskScanProvider; } public DiskScanJob() @@ -52,7 +52,7 @@ namespace NzbDrone.Core.Providers.Jobs try { notification.CurrentMessage = string.Format("Scanning disk for '{0}'", series.Title); - _mediaFileProvider.Scan(series); + _diskScanProvider.Scan(series); notification.CurrentMessage = string.Format("Media File Scan completed for '{0}'", series.Title); } catch (Exception e) diff --git a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs index 7ca43f30f..599dad96b 100644 --- a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs +++ b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs @@ -11,17 +11,17 @@ namespace NzbDrone.Core.Providers.Jobs { private readonly ConfigProvider _configProvider; private readonly DiskProvider _diskProvider; - private readonly MediaFileProvider _mediaFileProvider; + private readonly DiskScanProvider _diskScanProvider; private readonly SeriesProvider _seriesProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] public PostDownloadScanJob(ConfigProvider configProvider, DiskProvider diskProvider, - MediaFileProvider mediaFileProvider, SeriesProvider seriesProvider) + DiskScanProvider diskScanProvider, SeriesProvider seriesProvider) { _configProvider = configProvider; _diskProvider = diskProvider; - _mediaFileProvider = mediaFileProvider; + _diskScanProvider = diskScanProvider; _seriesProvider = seriesProvider; } @@ -84,7 +84,7 @@ namespace NzbDrone.Core.Providers.Jobs return; } - _mediaFileProvider.ImportNewFiles(subfolder, series); + _diskScanProvider.Scan(series, subfolder); } Logger.Debug("New Download Scan Job completed successfully"); } diff --git a/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs b/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs index e9f4373d9..cc39c455a 100644 --- a/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs +++ b/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs @@ -7,21 +7,17 @@ namespace NzbDrone.Core.Providers.Jobs { public class RenameEpisodeJob : IJob { - private readonly DiskProvider _diskProvider; - private readonly EpisodeProvider _episodeProvider; + private readonly DiskScanProvider _diskScanProvider; private readonly MediaFileProvider _mediaFileProvider; - private readonly SeriesProvider _seriesProvider; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] - public RenameEpisodeJob(DiskProvider diskProvider, EpisodeProvider episodeProvider, - MediaFileProvider mediaFileProvider, SeriesProvider seriesProvider) + public RenameEpisodeJob(DiskScanProvider diskScanProvider, MediaFileProvider mediaFileProvider) { - _diskProvider = diskProvider; - _episodeProvider = episodeProvider; + _diskScanProvider = diskScanProvider; _mediaFileProvider = mediaFileProvider; - _seriesProvider = seriesProvider; } public string Name @@ -36,8 +32,8 @@ namespace NzbDrone.Core.Providers.Jobs public void Start(ProgressNotification notification, int targetId) { - var episode = _mediaFileProvider.GetEpisodeFile(targetId); - _mediaFileProvider.RenameEpisodeFile(episode); + var episode = _mediaFileProvider.GetEpisodeFile(targetId); + _diskScanProvider.RenameEpisodeFile(episode); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index 4e003eeb7..d510949cb 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -16,170 +16,27 @@ namespace NzbDrone.Core.Providers public class MediaFileProvider { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private static readonly string[] MediaExtentions = new[] { ".mkv", ".avi", ".wmv", ".mp4" }; - private readonly DiskProvider _diskProvider; private readonly EpisodeProvider _episodeProvider; - private readonly SeriesProvider _seriesProvider; private readonly ConfigProvider _configProvider; private readonly IDatabase _database; [Inject] - public MediaFileProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider, - SeriesProvider seriesProvider, ConfigProvider configProvider, - IDatabase database) + public MediaFileProvider(EpisodeProvider episodeProvider, ConfigProvider configProvider, IDatabase database) { - _diskProvider = diskProvider; _episodeProvider = episodeProvider; - _seriesProvider = seriesProvider; _configProvider = configProvider; _database = database; } public MediaFileProvider() { } - /// - /// Scans the specified series folder for media files - /// - /// The series to be scanned - public virtual List Scan(Series series) - { - if (_episodeProvider.GetEpisodeBySeries(series.SeriesId).Count == 0) - { - Logger.Debug("Series {0} has no episodes. skipping", series.Title); - return new List(); - } - - var mediaFileList = GetVideoFiles(series.Path); - var fileList = new List(); - - foreach (var filePath in mediaFileList) - { - var file = ImportFile(series, filePath); - if (file != null) - fileList.Add(file); - } - series.LastDiskSync = DateTime.Now; - _seriesProvider.UpdateSeries(series); - return fileList; - } - public virtual EpisodeFile ImportFile(Series series, string filePath) - { - Logger.Trace("Importing file to database [{0}]", filePath); - if (_database.Exists("Path =@0", Parser.NormalizePath(filePath))) - { - Logger.Trace("[{0}] already exists in the database. skipping.", filePath); - return null; - } - - var size = _diskProvider.GetSize(filePath); - - //If Size is less than 50MB and contains sample. Check for Size to ensure its not an episode with sample in the title - if (size < 40000000 && filePath.ToLower().Contains("sample")) - { - Logger.Trace("[{0}] appears to be a sample. skipping.", filePath); - return null; - } - var parseResult = Parser.ParseEpisodeInfo(filePath); - if (parseResult == null) - return null; - parseResult.CleanTitle = series.Title;//replaces the nasty path as title to help with logging - - //Stores the list of episodes to add to the EpisodeFile - var episodes = new List(); - - //Check for daily shows - if (parseResult.EpisodeNumbers == null) - { - var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.AirDate.Date); - - if (episode != null) - { - episodes.Add(episode); - } - else - { - Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); - } - } - else - { - foreach (var episodeNumber in parseResult.EpisodeNumbers) - { - var episode = _episodeProvider.GetEpisode(series.SeriesId, parseResult.SeasonNumber, - episodeNumber); - - if (episode != null) - { - episodes.Add(episode); - } - else - { - Logger.Warn("Unable to find [{0}] in the database.[{1}]", parseResult, filePath); - } - } - } - - //Return null if no Episodes exist in the DB for the parsed episodes from file - if (episodes.Count <= 0) - return null; - - var episodeFile = new EpisodeFile(); - episodeFile.DateAdded = DateTime.Now; - episodeFile.SeriesId = series.SeriesId; - episodeFile.Path = Parser.NormalizePath(filePath); - episodeFile.Size = size; - episodeFile.Quality = parseResult.Quality.QualityType; - episodeFile.Proper = parseResult.Quality.Proper; - episodeFile.SeasonNumber = parseResult.SeasonNumber; - var fileId = Convert.ToInt32(_database.Insert(episodeFile)); - - //This is for logging + updating the episodes that are linked to this EpisodeFile - string episodeList = String.Empty; - foreach (var ep in episodes) - { - ep.EpisodeFileId = fileId; - _episodeProvider.UpdateEpisode(ep); - episodeList += String.Format(", {0}", ep.EpisodeId).Trim(' ', ','); - } - Logger.Trace("File {0}:{1} attached to episode(s): '{2}'", episodeFile.EpisodeFileId, filePath, - episodeList); - - return episodeFile; - - } - - /// - /// Removes files that no longer exist from the database - /// - /// list of files to verify - public virtual void CleanUp(List files) - { - //TODO: remove orphaned files. in files table but not linked to from episode table. - foreach (var episodeFile in files) - { - if (!_diskProvider.FileExists(episodeFile.Path)) - { - Logger.Trace("File {0} no longer exists on disk. removing from database.", episodeFile.Path); - - //Set the EpisodeFileId for each episode attached to this file to 0 - foreach (var episode in episodeFile.Episodes) - { - episode.EpisodeFileId = 0; - _episodeProvider.UpdateEpisode(episode); - } - - //Delete it from the DB - _database.Delete(episodeFile.EpisodeFileId); - } - } - } public virtual void Update(EpisodeFile episodeFile) { @@ -211,98 +68,20 @@ namespace NzbDrone.Core.Providers return new Tuple(avilableEpisodes.Count, episodeTotal.Count); } - private List GetVideoFiles(string path) - { - Logger.Debug("Scanning '{0}' for episodes", path); - - var filesOnDisk = _diskProvider.GetFiles(path, "*.*", SearchOption.AllDirectories); - - var mediaFileList = filesOnDisk.Where(c => MediaExtentions.Contains(Path.GetExtension(c).ToLower())).ToList(); - - Logger.Debug("{0} media files were found in {1}", mediaFileList.Count, path); - return mediaFileList; - } - - public virtual List ImportNewFiles(string path, Series series) + public virtual FileInfo CalculateFilePath(Series series, int seasonNumber, string fileName, string extention) { - var result = new List(); - - //Get all the files except those that are considered samples - var files = GetVideoFiles(path).Where(f => _diskProvider.GetSize(f) > 40000000 || !f.ToLower().Contains("sample")).ToList(); - - foreach (var file in files) + var path = series.Path; + if (series.SeasonFolder) { - try - { - //Parse the filename - var parseResult = Parser.ParseEpisodeInfo(Path.GetFileName(file)); - parseResult.Series = series; - parseResult.Episodes = _episodeProvider.GetEpisodes(parseResult); - - if (parseResult.Episodes.Count == 0) - { - Logger.Error("File '{0}' contains invalid episode information, skipping import", file); - continue; - } - - var ext = _diskProvider.GetExtension(file); - var filename = GetNewFilename(parseResult.Episodes, series.Title, parseResult.Quality.QualityType) + ext; - var folder = series.Path + Path.DirectorySeparatorChar; - if (_configProvider.UseSeasonFolder) - folder += _configProvider.SeasonFolderFormat - .Replace("%0s", parseResult.SeasonNumber.ToString("00")) - .Replace("%s", parseResult.SeasonNumber.ToString()) - + Path.DirectorySeparatorChar; - - _diskProvider.CreateDirectory(folder); - - //Get a list of episodeFiles that we need to delete and cleanup - var episodeFilesToClean = new List(); - - foreach (var episode in parseResult.Episodes) - { - if (episode.EpisodeFileId > 0) - episodeFilesToClean.Add(episode.EpisodeFile); - } - - if (episodeFilesToClean.Count != episodeFilesToClean.Where(e => parseResult.Quality.QualityType >= e.Quality).Count()) - { - Logger.Debug("Episode isn't an upgrade for all episodes in file: [{0}]. Skipping.", file); - continue; - } - - //Delete the files and then cleanup! - foreach (var e in episodeFilesToClean) - { - if (_diskProvider.FileExists(e.Path)) - _diskProvider.DeleteFile(e.Path); - } - - CleanUp(episodeFilesToClean); - - //Move the file - _diskProvider.RenameFile(file, folder + filename); - - //Import into DB - result.Add(ImportFile(series, folder + filename)); - } - - catch (Exception ex) - { - Logger.WarnException("Error importing new download: " + file, ex); - } + path = Path.Combine(path, "Season " + seasonNumber); } - //If we have imported all the non-sample files, delete the folder, requires a minimum of 1 file to be imported. - if (files.Count() > 0 && files.Count() == result.Count) - { - Logger.Debug("All non-sample files have been processed, deleting folder: {0}", path); - _diskProvider.DeleteFolder(path, true); - } + path = Path.Combine(path, fileName + extention); - return result; + return new FileInfo(path); } + public virtual string GetNewFilename(IList episodes, string seriesTitle, QualityTypes quality) { var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SeparatorStyle); @@ -361,40 +140,10 @@ namespace NzbDrone.Core.Providers return result.Trim(); } - public virtual FileInfo CalculateFilePath(Series series, int seasonNumber, string fileName, string extention) - { - var path = series.Path; - if (series.SeasonFolder) - { - path = Path.Combine(path, "Season " + seasonNumber); - } - - path = Path.Combine(path, fileName + extention); - - return new FileInfo(path); - } - - public virtual bool RenameEpisodeFile(EpisodeFile episodeFile) - { - if (episodeFile == null) - throw new ArgumentNullException("episodeFile"); - - var series = _seriesProvider.GetSeries(episodeFile.SeriesId); - var ext = _diskProvider.GetExtension(episodeFile.Path); - var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId); - var newFileName = GetNewFilename(episodes, series.Title, episodeFile.Quality); - var newFile = CalculateFilePath(series, episodes.First().SeasonNumber, newFileName, ext); - //Do the rename - _diskProvider.RenameFile(episodeFile.Path, newFile.FullName); - //Update the filename in the DB - episodeFile.Path = newFile.FullName; - Update(episodeFile); - return true; - } } } \ No newline at end of file From 8b7c624d09f533e12c786709d44b0325832596ce Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 20:25:04 -0700 Subject: [PATCH 4/8] Reformated some files --- NzbDrone.Core/Providers/DiskScanProvider.cs | 35 +++++++------------- NzbDrone.Core/Providers/MediaFileProvider.cs | 28 ++++------------ 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 996be81b7..49a491129 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -2,33 +2,28 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using Ninject; using NLog; -using NzbDrone.Core.Helpers; -using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; -using NzbDrone.Core.Repository.Quality; using PetaPoco; namespace NzbDrone.Core.Providers { public class DiskScanProvider { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private static readonly string[] MediaExtentions = new[] { ".mkv", ".avi", ".wmv", ".mp4" }; + private static readonly string[] MediaExtentions = new[] {".mkv", ".avi", ".wmv", ".mp4"}; + private readonly IDatabase _database; private readonly DiskProvider _diskProvider; private readonly EpisodeProvider _episodeProvider; - private readonly SeriesProvider _seriesProvider; private readonly MediaFileProvider _mediaFileProvider; - private readonly IDatabase _database; + private readonly SeriesProvider _seriesProvider; [Inject] public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider, - SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider, - IDatabase database) + SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider, + IDatabase database) { _diskProvider = diskProvider; _episodeProvider = episodeProvider; @@ -40,7 +35,6 @@ namespace NzbDrone.Core.Providers public DiskScanProvider() { - } /// @@ -92,7 +86,7 @@ namespace NzbDrone.Core.Providers return null; } - var size = _diskProvider.GetSize(filePath); + long size = _diskProvider.GetSize(filePath); //If Size is less than 50MB and contains sample. Check for Size to ensure its not an episode with sample in the title if (size < 40000000 && filePath.ToLower().Contains("sample")) @@ -106,7 +100,7 @@ namespace NzbDrone.Core.Providers if (parseResult == null) return null; - parseResult.CleanTitle = series.Title;//replaces the nasty path as title to help with logging + parseResult.CleanTitle = series.Title; //replaces the nasty path as title to help with logging //Stores the list of episodes to add to the EpisodeFile var episodes = new List(); @@ -155,7 +149,7 @@ namespace NzbDrone.Core.Providers episodeFile.Quality = parseResult.Quality.QualityType; episodeFile.Proper = parseResult.Quality.Proper; episodeFile.SeasonNumber = parseResult.SeasonNumber; - var fileId = Convert.ToInt32(_database.Insert(episodeFile)); + int fileId = Convert.ToInt32(_database.Insert(episodeFile)); //This is for logging + updating the episodes that are linked to this EpisodeFile string episodeList = String.Empty; @@ -169,21 +163,18 @@ namespace NzbDrone.Core.Providers episodeList); return episodeFile; - } - - public virtual bool RenameEpisodeFile(EpisodeFile episodeFile) { if (episodeFile == null) throw new ArgumentNullException("episodeFile"); var series = _seriesProvider.GetSeries(episodeFile.SeriesId); - var ext = _diskProvider.GetExtension(episodeFile.Path); + string ext = _diskProvider.GetExtension(episodeFile.Path); var episodes = _episodeProvider.GetEpisodesByFileId(episodeFile.EpisodeFileId); - var newFileName = _mediaFileProvider.GetNewFilename(episodes, series.Title, episodeFile.Quality); + string newFileName = _mediaFileProvider.GetNewFilename(episodes, series.Title, episodeFile.Quality); var newFile = _mediaFileProvider.CalculateFilePath(series, episodes.First().SeasonNumber, newFileName, ext); @@ -199,9 +190,8 @@ namespace NzbDrone.Core.Providers } - /// - /// Removes files that no longer exist from the database + /// Removes files that no longer exist on disk from the database /// /// list of files to verify public virtual void CleanUp(List files) @@ -238,6 +228,5 @@ namespace NzbDrone.Core.Providers Logger.Debug("{0} media files were found in {1}", mediaFileList.Count, path); return mediaFileList; } - } -} +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index d510949cb..732d3a18d 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -5,7 +5,6 @@ using System.Linq; using Ninject; using NLog; using NzbDrone.Core.Helpers; -using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; @@ -16,9 +15,9 @@ namespace NzbDrone.Core.Providers public class MediaFileProvider { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private readonly EpisodeProvider _episodeProvider; private readonly ConfigProvider _configProvider; private readonly IDatabase _database; + private readonly EpisodeProvider _episodeProvider; [Inject] public MediaFileProvider(EpisodeProvider episodeProvider, ConfigProvider configProvider, IDatabase database) @@ -28,15 +27,9 @@ namespace NzbDrone.Core.Providers _database = database; } - public MediaFileProvider() { } - - - - - - - - + public MediaFileProvider() + { + } public virtual void Update(EpisodeFile episodeFile) { @@ -70,7 +63,7 @@ namespace NzbDrone.Core.Providers public virtual FileInfo CalculateFilePath(Series series, int seasonNumber, string fileName, string extention) { - var path = series.Path; + string path = series.Path; if (series.SeasonFolder) { path = Path.Combine(path, "Season " + seasonNumber); @@ -81,15 +74,14 @@ namespace NzbDrone.Core.Providers return new FileInfo(path); } - public virtual string GetNewFilename(IList episodes, string seriesTitle, QualityTypes quality) { var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SeparatorStyle); var numberStyle = EpisodeSortingHelper.GetNumberStyle(_configProvider.NumberStyle); - var episodeNames = episodes[0].Title; + string episodeNames = episodes[0].Title; - var result = String.Empty; + string result = String.Empty; if (_configProvider.SeriesName) { @@ -139,11 +131,5 @@ namespace NzbDrone.Core.Providers Logger.Debug("New File Name is: {0}", result.Trim()); return result.Trim(); } - - - - - - } } \ No newline at end of file From 45549fa50b6e4730452e0e4c32386ffa000e4e18 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 20:25:24 -0700 Subject: [PATCH 5/8] Fixed attach series issue and null episodes. --- NzbDrone.Core.Test/EpisodeProviderTest.cs | 59 ++++++++++++++++++++++ NzbDrone.Core/Providers/EpisodeProvider.cs | 1 + 2 files changed, 60 insertions(+) diff --git a/NzbDrone.Core.Test/EpisodeProviderTest.cs b/NzbDrone.Core.Test/EpisodeProviderTest.cs index c931c8a93..466612259 100644 --- a/NzbDrone.Core.Test/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/EpisodeProviderTest.cs @@ -45,6 +45,51 @@ namespace NzbDrone.Core.Test episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); } + + [Test] + public void GetEpisodes_by_season_episode_exists() + { + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var fakeSeries = Builder.CreateNew().Build(); + var fakeEpisodes = Builder.CreateNew() + .With(e => e.SeriesId = fakeSeries.SeriesId) + .With(e => e.EpisodeNumber = 1) + .And(e => e.SeasonNumber = 2).Build(); + + + db.Insert(fakeEpisodes); + + mocker.GetMock() + .Setup(p => p.GetSeries(1)) + .Returns(fakeSeries); + + //Act + var episode = mocker.Resolve().GetEpisode(fakeSeries.SeriesId, 2, 1); + + //Assert + episode.ShouldHave().AllPropertiesBut(e => e.Series).EqualTo(fakeEpisodes); + episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); + } + + [Test] + public void GetEpisodes_by_season_episode_doesnt_exists() + { + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + + + //Act + var episode = mocker.Resolve().GetEpisode(1, 1, 1); + + //Assert + episode.Should().BeNull(); + } + [Test] [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Sequence contains no elements")] public void GetEpisodes_invalid_series() @@ -103,6 +148,20 @@ namespace NzbDrone.Core.Test returnedSeries.Should().BeEquivalentTo(fakeEpisodes); } + [Test] + public void AttachSeries_null_episode_should_return_null() + { + var mocker = new AutoMoqer(); + + Episode episode = null; + + //Act + var result = mocker.Resolve().AttachSeries(episode); + + //Assert + result.Should().BeNull(); + } + [Test] public void AttachSeries_single_success() { diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 006d1d8e9..16e965abc 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -236,6 +236,7 @@ namespace NzbDrone.Core.Providers public Episode AttachSeries(Episode episode) { + if (episode == null) return episode; episode.Series = _seriesProvider.GetSeries(episode.SeriesId); return episode; } From 907c508a70b6ef20accbe3fc8ad88672b4a8b974 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 22:08:58 -0700 Subject: [PATCH 6/8] PetaPoco now defaults to SQLite, requires WHERE on exists calls --- NzbDrone.Core.Test/QualityProfileTest.cs | 35 ++++++++++++++++++++ NzbDrone.Core/Datastore/PetaPoco/PetaPoco.cs | 8 ++--- NzbDrone.Core/Providers/DiskScanProvider.cs | 2 +- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/NzbDrone.Core.Test/QualityProfileTest.cs b/NzbDrone.Core.Test/QualityProfileTest.cs index f1b04f53c..af617b935 100644 --- a/NzbDrone.Core.Test/QualityProfileTest.cs +++ b/NzbDrone.Core.Test/QualityProfileTest.cs @@ -1,9 +1,11 @@ // ReSharper disable RedundantUsingDirective using System; using System.Collections.Generic; +using AutoMoq; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; +using NzbDrone.Core.Providers; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Test.Framework; @@ -63,6 +65,39 @@ namespace NzbDrone.Core.Test fetch.Allowed.Should().HaveCount(0); } + + [Test] + public void Update_Success() + { + //Arrange + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var testProfile = new QualityProfile + { + Name = Guid.NewGuid().ToString(), + Cutoff = QualityTypes.SDTV + }; + + //Act + var id = Convert.ToInt32(db.Insert(testProfile)); + var currentProfile = db.SingleOrDefault(id); + + + //Update + currentProfile.Cutoff = QualityTypes.Bluray720p; + mocker.Resolve().Update(currentProfile); + + var updated = mocker.Resolve().Get(currentProfile.QualityProfileId); + + //Assert + updated.Name.Should().Be(currentProfile.Name); + updated.Cutoff.Should().Be(QualityTypes.Bluray720p); + updated.AllowedString.Should().Be(currentProfile.AllowedString); + + } + [Test] public void Test_Series_Quality() { diff --git a/NzbDrone.Core/Datastore/PetaPoco/PetaPoco.cs b/NzbDrone.Core/Datastore/PetaPoco/PetaPoco.cs index 0d040ef12..dd96d0737 100644 --- a/NzbDrone.Core/Datastore/PetaPoco/PetaPoco.cs +++ b/NzbDrone.Core/Datastore/PetaPoco/PetaPoco.cs @@ -316,7 +316,7 @@ namespace PetaPoco Oracle, SQLite } - DBType _dbType = DBType.SqlServer; + DBType _dbType = DBType.SQLite; // Common initialization private void CommonConstruct() @@ -1256,18 +1256,18 @@ namespace PetaPoco case DBType.SQLite: case DBType.MySql: { - existsTemplate = "SELECT EXISTS (SELECT 1 FROM {0} WHERE {1})"; + existsTemplate = "SELECT EXISTS (SELECT 1 FROM {0} {1})"; break; } case DBType.SqlServer: { - existsTemplate = "IF EXISTS (SELECT 1 FROM {0} WHERE {1}) SELECT 1 ELSE SELECT 0"; + existsTemplate = "IF EXISTS (SELECT 1 FROM {0} {1}) SELECT 1 ELSE SELECT 0"; break; } default: { - existsTemplate = "SELECT COUNT(*) FROM {0} WHERE {1}"; + existsTemplate = "SELECT COUNT(*) FROM {0} {1}"; break; } } diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 49a491129..7db6809b5 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -80,7 +80,7 @@ namespace NzbDrone.Core.Providers { Logger.Trace("Importing file to database [{0}]", filePath); - if (_database.Exists("Path =@0", Parser.NormalizePath(filePath))) + if (_database.Exists("WHERE Path =@0", Parser.NormalizePath(filePath))) { Logger.Trace("[{0}] already exists in the database. skipping.", filePath); return null; From ef9bbdc60ed7514bf0cc92c8b4ff3322a1e7082e Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 23:28:42 -0700 Subject: [PATCH 7/8] Removed leftover code from old ages ;) --- NzbDrone.Core.Test/SeriesProviderTest.cs | Bin 9493 -> 8228 bytes NzbDrone.Core/Providers/SeriesProvider.cs | 11 ---- NzbDrone.Core/Providers/TvDbProvider.cs | 66 ---------------------- 3 files changed, 77 deletions(-) diff --git a/NzbDrone.Core.Test/SeriesProviderTest.cs b/NzbDrone.Core.Test/SeriesProviderTest.cs index 9064eb8d57faabac5a03b5a86a0302715a82ced5..bced5c070b2389c8a7c02e700326959ad14fa62b 100644 GIT binary patch delta 29 lcmbR0wZvfq7t`bhN!HC@*cNe5{w2aS*-cVt^FE<)Apod33Z?)6 delta 718 zcmZ{i-!20|6vm^WXlb?er$`tpVjHz}E!C)0WFlyDY z6w025?Lz_&vDr?wsny^;HYez4sf2V%X$d!RouDNos0KlbS$C;pQvp(A8Q4$8;U$@# z(eV*c2=)#Pu3KrCmPWjy4+|c*32j&mPR1rDrCpO5BK7f9k~J<5YJEfe?o*{UO`C;~ z_WK=PM+aL*wDAGB&CbJPBpH`0nBf|xq$K*t1)hX8T=6s3>WN7iEe42^C^Jc>W&5p00|w~1cJvxy0ro#?CPe)j8l1;Q zhI!?Nb8y7R_PfRWe=S1ekhMIYB)8eJ{<}%*yLqHOX@~!)ZsM9nM0ksi!c{gC$F57X zz%@J#Pv<4RD_eY3ROd6wvQSyvQ+JSJILCCaeGT5CLvYrefr~`9@OOk<(DbxySt4|F fq3n c.SeriesId == id && c.Monitored); } - public virtual TvdbSeries MapPathToSeries(string path) - { - var seriesPath = new DirectoryInfo(path); - var searchResults = _tvDbProvider.GetSeries(seriesPath.Name); - - if (searchResults == null) - return null; - - return _tvDbProvider.GetSeries(searchResults.Id, false); - } - public virtual Series UpdateSeriesInfo(int seriesId) { var tvDbSeries = _tvDbProvider.GetSeries(seriesId, true); diff --git a/NzbDrone.Core/Providers/TvDbProvider.cs b/NzbDrone.Core/Providers/TvDbProvider.cs index 7b3a91ac1..9225a9c69 100644 --- a/NzbDrone.Core/Providers/TvDbProvider.cs +++ b/NzbDrone.Core/Providers/TvDbProvider.cs @@ -38,43 +38,6 @@ namespace NzbDrone.Core.Providers } - public virtual TvdbSearchResult GetSeries(string title) - { - lock (_handler) - { - var searchResults = SearchSeries(title); - if (searchResults.Count == 0) - return null; - - foreach (var tvdbSearchResult in searchResults) - { - if (IsTitleMatch(tvdbSearchResult.SeriesName, title)) - { - Logger.Debug("Search for '{0}' was successful", title); - return tvdbSearchResult; - } - } - } - return null; - } - - public virtual int GetBestMatch(List searchResults, string title) - { - if (searchResults.Count == 0) - return 0; - - foreach (var tvdbSearchResult in searchResults) - { - if (IsTitleMatch(tvdbSearchResult.SeriesName, title)) - { - Logger.Debug("Search for '{0}' was successful", title); - return tvdbSearchResult.Id; - } - } - - return searchResults[0].Id; - } - public virtual TvdbSeries GetSeries(int id, bool loadEpisodes) { lock (_handler) @@ -116,34 +79,5 @@ namespace NzbDrone.Core.Providers } } - /// - /// Determines whether a title in a search result is equal to the title searched for. - /// - /// Name of the directory. - /// The TVDB title. - /// - /// true if the titles are found to be same; otherwise, false. - /// - public static bool IsTitleMatch(string directoryName, string tvdbTitle) - { - var result = false; - - if (String.IsNullOrEmpty(directoryName)) - throw new ArgumentException("directoryName"); - if (String.IsNullOrEmpty(tvdbTitle)) - throw new ArgumentException("tvdbTitle"); - - if (String.Equals(directoryName, tvdbTitle, StringComparison.CurrentCultureIgnoreCase)) - { - result = true; - } - else if (String.Equals(CleanUpRegex.Replace(directoryName, ""), CleanUpRegex.Replace(tvdbTitle, ""), - StringComparison.InvariantCultureIgnoreCase)) - result = true; - - Logger.Debug("Match between '{0}' and '{1}' was {2}", tvdbTitle, directoryName, result); - - return result; - } } } \ No newline at end of file From f0fb5aa1581e34a8c31311ec9edffa373e32be2d Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 19 Jun 2011 23:30:47 -0700 Subject: [PATCH 8/8] Removed leftover code from old ages ;) --- NzbDrone.Core.Test/TvDbProviderTest.cs | 40 -------------------------- 1 file changed, 40 deletions(-) diff --git a/NzbDrone.Core.Test/TvDbProviderTest.cs b/NzbDrone.Core.Test/TvDbProviderTest.cs index f0e12e882..6df4a4246 100644 --- a/NzbDrone.Core.Test/TvDbProviderTest.cs +++ b/NzbDrone.Core.Test/TvDbProviderTest.cs @@ -25,38 +25,10 @@ namespace NzbDrone.Core.Test result[0].SeriesName.Should().Be(title); } - [TestCase("The Simpsons")] - [TestCase("Family Guy")] - [TestCase("South Park")] - public void successful_title_lookup(string title) - { - var tvCont = new TvDbProvider(); - var result = tvCont.GetSeries(title); - result.SeriesName.Should().Be(title); - } - [TestCase(new object[] { "CAPITAL", "capital", true })] - [TestCase(new object[] { "Something!!", "Something", true })] - [TestCase(new object[] { "Simpsons 2000", "Simpsons", true })] - [TestCase(new object[] { "Simp222sons", "Simpsons", true })] - [TestCase(new object[] { "Simpsons", "The Simpsons", true })] - [TestCase(new object[] { "Law and order", "Law & order", true })] - [TestCase(new object[] { "xxAndxx", "xxxx", false })] - [TestCase(new object[] { "Andxx", "xx", false })] - [TestCase(new object[] { "xxAnd", "xx", false })] - [TestCase(new object[] { "Thexx", "xx", false })] - [TestCase(new object[] { "Thexx", "xx", false })] - [TestCase(new object[] { "xxThexx", "xxxxx", false })] - [TestCase(new object[] { "Simpsons The", "Simpsons", true })] - public void Name_match_test(string a, string b, bool match) - { - bool result = TvDbProvider.IsTitleMatch(a, b); - - Assert.AreEqual(match, result, "{0} , {1}", a, b); - } [Test] public void no_search_result() @@ -71,18 +43,6 @@ namespace NzbDrone.Core.Test result.Should().BeEmpty(); } - [Test] - public void no_result_title_lookup() - { - //setup - var tvdbProvider = new TvDbProvider(); - - //act - var result = tvdbProvider.GetSeries("clone high"); - - //assert - Assert.IsNull(result); - } [Test] public void none_unique_season_episode_number()