From 8876c9194d35485222b26e3a4f859fec5848a428 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 30 Jan 2021 14:24:33 -0800 Subject: [PATCH] New: Show preferred word score in history --- .../History/Details/HistoryDetails.js | 34 ++- frontend/src/Activity/History/HistoryRow.css | 6 + frontend/src/Activity/History/HistoryRow.js | 12 ++ .../Table/TableOptions/TableOptionsModal.js | 4 +- .../InteractiveSearch/InteractiveSearchRow.js | 4 +- frontend/src/Store/Actions/historyActions.js | 13 +- .../Number/formatPreferredWordScore.js | 16 ++ .../EpisodeImport/GetSceneNameFixture.cs | 199 ++++++++++++++++++ .../ImportApprovedEpisodesFixture.cs | 108 +--------- .../UpgradeSpecificationFixture.cs | 15 +- src/NzbDrone.Core/History/EpisodeHistory.cs | 2 - src/NzbDrone.Core/History/HistoryService.cs | 11 +- .../Aggregation/AggregationService.cs | 3 + .../AggregatePreferredWordScore.cs | 37 ++++ .../EpisodeImport/ImportApprovedEpisodes.cs | 36 ---- .../EpisodeImport/SceneNameCalculator.cs | 39 ++++ .../Specifications/UpgradeSpecification.cs | 30 +-- .../Parser/Model/LocalEpisode.cs | 4 +- 18 files changed, 377 insertions(+), 196 deletions(-) create mode 100644 frontend/src/Utilities/Number/formatPreferredWordScore.js create mode 100644 src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/GetSceneNameFixture.cs rename src/NzbDrone.Core.Test/MediaFiles/{ => EpisodeImport}/ImportApprovedEpisodesFixture.cs (75%) create mode 100644 src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregatePreferredWordScore.cs create mode 100644 src/NzbDrone.Core/MediaFiles/EpisodeImport/SceneNameCalculator.cs diff --git a/frontend/src/Activity/History/Details/HistoryDetails.js b/frontend/src/Activity/History/Details/HistoryDetails.js index 361844b77..0da11f141 100644 --- a/frontend/src/Activity/History/Details/HistoryDetails.js +++ b/frontend/src/Activity/History/Details/HistoryDetails.js @@ -1,7 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; -import formatDateTime from 'Utilities/Date/formatDateTime'; import formatAge from 'Utilities/Number/formatAge'; +import formatDateTime from 'Utilities/Date/formatDateTime'; +import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore'; import Link from 'Components/Link/Link'; import DescriptionList from 'Components/DescriptionList/DescriptionList'; import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem'; @@ -22,6 +23,7 @@ function HistoryDetails(props) { const { indexer, releaseGroup, + preferredWordScore, nzbInfoUrl, downloadClient, downloadId, @@ -56,6 +58,14 @@ function HistoryDetails(props) { /> } + { + !!preferredWordScore && + + } + { !!nzbInfoUrl && @@ -86,7 +96,7 @@ function HistoryDetails(props) { } { - !!indexer && + !!(age || ageHours || ageMinutes) && } + + { + !!preferredWordScore && + + } ); } if (eventType === 'episodeFileDeleted') { const { - reason + reason, + preferredWordScore } = data; let reasonMessage = ''; @@ -195,6 +215,14 @@ function HistoryDetails(props) { title="Reason" data={reasonMessage} /> + + { + !!preferredWordScore && + + } ); } diff --git a/frontend/src/Activity/History/HistoryRow.css b/frontend/src/Activity/History/HistoryRow.css index 669377fdb..07a392d71 100644 --- a/frontend/src/Activity/History/HistoryRow.css +++ b/frontend/src/Activity/History/HistoryRow.css @@ -10,6 +10,12 @@ width: 80px; } +.preferredWordScore { + composes: cell from '~Components/Table/Cells/TableRowCell.css'; + + width: 55px; +} + .releaseGroup { composes: cell from '~Components/Table/Cells/TableRowCell.css'; diff --git a/frontend/src/Activity/History/HistoryRow.js b/frontend/src/Activity/History/HistoryRow.js index 454913412..7b892be6a 100644 --- a/frontend/src/Activity/History/HistoryRow.js +++ b/frontend/src/Activity/History/HistoryRow.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore'; import { icons } from 'Helpers/Props'; import IconButton from 'Components/Link/IconButton'; import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector'; @@ -194,6 +195,17 @@ class HistoryRow extends Component { ); } + if (name === 'preferredWordScore') { + return ( + + {formatPreferredWordScore(data.preferredWordScore)} + + ); + } + if (name === 'releaseGroup') { return ( - {preferredWordScore > 0 && `+${preferredWordScore}`} - {preferredWordScore < 0 && preferredWordScore} + {formatPreferredWordScore(preferredWordScore)} diff --git a/frontend/src/Store/Actions/historyActions.js b/frontend/src/Store/Actions/historyActions.js index abe9c16bf..3d4a52d13 100644 --- a/frontend/src/Store/Actions/historyActions.js +++ b/frontend/src/Store/Actions/historyActions.js @@ -1,7 +1,9 @@ +import React from 'react'; import { createAction } from 'redux-actions'; import createAjaxRequest from 'Utilities/createAjaxRequest'; import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers'; -import { filterTypes, sortDirections } from 'Helpers/Props'; +import { filterTypes, icons, sortDirections } from 'Helpers/Props'; +import Icon from 'Components/Icon'; import { createThunk, handleThunks } from 'Store/thunks'; import createClearReducer from './Creators/Reducers/createClearReducer'; import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer'; @@ -80,6 +82,15 @@ export const defaultState = { label: 'Release Group', isVisible: false }, + { + name: 'preferredWordScore', + columnLabel: 'Preferred Word Score', + label: React.createElement(Icon, { + name: icons.SCORE, + title: 'Preferred word score' + }), + isVisible: false + }, { name: 'details', columnLabel: 'Details', diff --git a/frontend/src/Utilities/Number/formatPreferredWordScore.js b/frontend/src/Utilities/Number/formatPreferredWordScore.js new file mode 100644 index 000000000..93024e0de --- /dev/null +++ b/frontend/src/Utilities/Number/formatPreferredWordScore.js @@ -0,0 +1,16 @@ + +function formatPreferredWordScore(input) { + const score = Number(input); + + if (score > 0) { + return `+${score}`; + } + + if (score < 0) { + return score; + } + + return ''; +} + +export default formatPreferredWordScore; diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/GetSceneNameFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/GetSceneNameFixture.cs new file mode 100644 index 000000000..ae64e2c12 --- /dev/null +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/GetSceneNameFixture.cs @@ -0,0 +1,199 @@ +using System.Collections.Generic; +using System.IO; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.MediaFiles.EpisodeImport; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Profiles.Qualities; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; +using NzbDrone.Core.Languages; +using NzbDrone.Core.Profiles.Languages; + +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport +{ + [TestFixture] + public class GetSceneNameFixture : CoreTest + { + private LocalEpisode _localEpisode; + private string _seasonName = "series.title.s02.dvdrip.x264-ingot"; + private string _episodeName = "series.title.s02e23.dvdrip.x264-ingot"; + + [SetUp] + public void Setup() + { + var series = Builder.CreateNew() + .With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() }) + .With(l => l.LanguageProfile = new LanguageProfile + { + Cutoff = Language.Spanish, + Languages = Languages.LanguageFixture.GetDefaultLanguages() + }) + .With(s => s.Path = @"C:\Test\TV\Series Title".AsOsAgnostic()) + .Build(); + + var episode = Builder.CreateNew() + .Build(); + + _localEpisode = new LocalEpisode + { + Series = series, + Episodes = new List {episode}, + Path = Path.Combine(series.Path, "Series Title - S02E23 - Episode Title.mkv"), + Quality = new QualityModel(Quality.Bluray720p), + ReleaseGroup = "DRONE" + }; + } + + private void GivenExistingFileOnDisk() + { + Mocker.GetMock() + .Setup(s => s.GetFilesWithRelativePath(It.IsAny(), It.IsAny())) + .Returns(new List()); + } + + [Test] + public void should_use_download_client_item_title_as_scene_name() + { + _localEpisode.DownloadClientEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = _episodeName + }; + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .Be(_episodeName); + } + + [Test] + public void should_not_use_download_client_item_title_as_scene_name_if_full_season() + { + _localEpisode.DownloadClientEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = _seasonName, + FullSeason = true + }; + + _localEpisode.Path = Path.Combine(@"C:\Test\Unsorted TV", _seasonName, _episodeName) + .AsOsAgnostic(); + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .BeNull(); + } + + [Test] + public void should_not_use_download_client_item_title_as_scene_name_if_there_are_other_video_files() + { + _localEpisode.OtherVideoFiles = true; + _localEpisode.DownloadClientEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = _seasonName, + FullSeason = false + }; + + _localEpisode.Path = Path.Combine(@"C:\Test\Unsorted TV", _seasonName, _episodeName) + .AsOsAgnostic(); + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .BeNull(); + } + + [Test] + public void should_use_file_name_as_scenename_only_if_it_looks_like_scenename() + { + _localEpisode.Path = Path.Combine(@"C:\Test\Unsorted TV", _seasonName, _episodeName + ".mkv") + .AsOsAgnostic(); + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .Be(_episodeName); + } + + [Test] + public void should_not_use_file_name_as_scenename_if_it_doesnt_look_like_scenename() + { + _localEpisode.Path = Path.Combine(@"C:\Test\Unsorted TV", _episodeName, "aaaaa.mkv") + .AsOsAgnostic(); + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .BeNull(); + } + + [Test] + public void should_use_folder_name_as_scenename_only_if_it_looks_like_scenename() + { + _localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = _episodeName + }; + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .Be(_episodeName); + } + + [Test] + public void should_not_use_folder_name_as_scenename_if_it_doesnt_look_like_scenename() + { + _localEpisode.Path = Path.Combine(@"C:\Test\Unsorted TV", _episodeName, "aaaaa.mkv") + .AsOsAgnostic(); + + _localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = "aaaaa" + }; + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .BeNull(); + } + + [Test] + public void should_not_use_folder_name_as_scenename_if_it_is_for_a_full_season() + { + _localEpisode.Path = Path.Combine(@"C:\Test\Unsorted TV", _episodeName, "aaaaa.mkv") + .AsOsAgnostic(); + + _localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = _seasonName, + FullSeason = true + }; + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .BeNull(); + } + + [Test] + public void should_not_use_folder_name_as_scenename_if_there_are_other_video_files() + { + _localEpisode.OtherVideoFiles = true; + _localEpisode.Path = Path.Combine(@"C:\Test\Unsorted TV", _episodeName, "aaaaa.mkv") + .AsOsAgnostic(); + + _localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = _seasonName, + FullSeason = false + }; + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .BeNull(); + } + + [TestCase(".mkv")] + [TestCase(".par2")] + [TestCase(".nzb")] + public void should_remove_extension_from_nzb_title_for_scene_name(string extension) + { + _localEpisode.DownloadClientEpisodeInfo = new ParsedEpisodeInfo + { + ReleaseTitle = _episodeName + extension + }; + + SceneNameCalculator.GetSceneName(_localEpisode).Should() + .Be(_episodeName); + } + } +} diff --git a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportApprovedEpisodesFixture.cs similarity index 75% rename from src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs rename to src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportApprovedEpisodesFixture.cs index 2a423c765..dd3cd5791 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportApprovedEpisodesFixture.cs @@ -21,7 +21,7 @@ using NzbDrone.Test.Common; using NzbDrone.Core.Languages; using NzbDrone.Core.Profiles.Languages; -namespace NzbDrone.Core.Test.MediaFiles +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport { [TestFixture] public class ImportApprovedEpisodesFixture : CoreTest @@ -169,112 +169,6 @@ namespace NzbDrone.Core.Test.MediaFiles Times.Never()); } - [Test] - public void should_use_nzb_title_as_scene_name() - { - GivenNewDownload(); - _downloadClientItem.Title = "malcolm.in.the.middle.s02e05.dvdrip.xvid-ingot"; - - Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == _downloadClientItem.Title))); - } - - [TestCase(".mkv")] - [TestCase(".par2")] - [TestCase(".nzb")] - public void should_remove_extension_from_nzb_title_for_scene_name(string extension) - { - GivenNewDownload(); - var title = "malcolm.in.the.middle.s02e05.dvdrip.xvid-ingot"; - - _downloadClientItem.Title = title + extension; - - Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == title))); - } - - [Test] - public void should_not_use_nzb_title_as_scene_name_if_full_season() - { - GivenNewDownload(); - _approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv"); - _downloadClientItem.Title = "malcolm.in.the.middle.s02.dvdrip.xvid-ingot"; - - Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == "malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot"))); - } - - [Test] - public void should_use_file_name_as_scenename_only_if_it_looks_like_scenename() - { - GivenNewDownload(); - _approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "series.title.s02e23.dvdrip.xvid-ingot.mkv"); - - Subject.Import(new List { _approvedDecisions.First() }, true); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == "series.title.s02e23.dvdrip.xvid-ingot"))); - } - - [Test] - public void should_not_use_file_name_as_scenename_if_it_doesnt_looks_like_scenename() - { - GivenNewDownload(); - _approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv"); - - Subject.Import(new List { _approvedDecisions.First() }, true); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == null))); - } - - [Test] - public void should_use_folder_name_as_scenename_only_if_it_looks_like_scenename() - { - GivenNewDownload(); - _approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv"); - _approvedDecisions.First().LocalEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo - { - ReleaseTitle = "series.title.s02e23.dvdrip.xvid-ingot" - }; - - Subject.Import(new List { _approvedDecisions.First() }, true); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == "series.title.s02e23.dvdrip.xvid-ingot"))); - } - - [Test] - public void should_not_use_folder_name_as_scenename_if_it_doesnt_looks_like_scenename() - { - GivenNewDownload(); - _approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv"); - _approvedDecisions.First().LocalEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo - { - ReleaseTitle = "aaaaa.mkv" - }; - - Subject.Import(new List { _approvedDecisions.First() }, true); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == null))); - } - - [Test] - public void should_not_use_folder_name_as_scenename_if_it_is_for_a_full_season() - { - GivenNewDownload(); - _approvedDecisions.First().LocalEpisode.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv"); - _approvedDecisions.First().LocalEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo - { - ReleaseTitle = "series.title.s02.dvdrip.xvid-ingot.mkv", - FullSeason = true - }; - - Subject.Import(new List { _approvedDecisions.First() }, true); - - Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.SceneName == null))); - } - [Test] public void should_import_larger_files_first() { diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs index b2f4c444a..ad844cca2 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs @@ -287,14 +287,11 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .Setup(s => s.DownloadPropersAndRepacks) .Returns(ProperDownloadTypes.DoNotPrefer); - Mocker.GetMock() - .Setup(s => s.Calculate(It.IsAny(), It.IsAny(), 0)) - .Returns(5); - Mocker.GetMock() .Setup(s => s.Calculate(It.IsAny(), It.IsAny())) .Returns(10); + _localEpisode.PreferredWordScore = 5; _localEpisode.Quality = new QualityModel(Quality.Bluray1080p); _localEpisode.Episodes = Builder.CreateListOfSize(1) @@ -364,14 +361,11 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .Setup(s => s.DownloadPropersAndRepacks) .Returns(ProperDownloadTypes.DoNotPrefer); - Mocker.GetMock() - .Setup(s => s.Calculate(It.IsAny(), It.IsAny(), 0)) - .Returns(5); - Mocker.GetMock() .Setup(s => s.Calculate(It.IsAny(), It.IsAny())) .Returns(1); + _localEpisode.PreferredWordScore = 5; _localEpisode.Quality = new QualityModel(Quality.Bluray1080p); _localEpisode.Episodes = Builder.CreateListOfSize(1) @@ -397,14 +391,11 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .Setup(s => s.DownloadPropersAndRepacks) .Returns(ProperDownloadTypes.DoNotPrefer); - Mocker.GetMock() - .Setup(s => s.Calculate(It.IsAny(), It.IsAny(), 0)) - .Returns(5); - Mocker.GetMock() .Setup(s => s.Calculate(It.IsAny(), It.IsAny())) .Returns(5); + _localEpisode.PreferredWordScore = 5; _localEpisode.Quality = new QualityModel(Quality.Bluray1080p); _localEpisode.Episodes = Builder.CreateListOfSize(1) diff --git a/src/NzbDrone.Core/History/EpisodeHistory.cs b/src/NzbDrone.Core/History/EpisodeHistory.cs index 933e05d7e..d6016cbde 100644 --- a/src/NzbDrone.Core/History/EpisodeHistory.cs +++ b/src/NzbDrone.Core/History/EpisodeHistory.cs @@ -26,9 +26,7 @@ namespace NzbDrone.Core.History public EpisodeHistoryEventType EventType { get; set; } public Dictionary Data { get; set; } public Language Language { get; set; } - public string DownloadId { get; set; } - } public enum EpisodeHistoryEventType diff --git a/src/NzbDrone.Core/History/HistoryService.cs b/src/NzbDrone.Core/History/HistoryService.cs index de1730bc6..274256974 100644 --- a/src/NzbDrone.Core/History/HistoryService.cs +++ b/src/NzbDrone.Core/History/HistoryService.cs @@ -39,11 +39,13 @@ namespace NzbDrone.Core.History IHandle { private readonly IHistoryRepository _historyRepository; + private readonly IEpisodeFilePreferredWordCalculator _episodeFilePreferredWordCalculator; private readonly Logger _logger; - public HistoryService(IHistoryRepository historyRepository, Logger logger) + public HistoryService(IHistoryRepository historyRepository, IEpisodeFilePreferredWordCalculator episodeFilePreferredWordCalculator, Logger logger) { _historyRepository = historyRepository; + _episodeFilePreferredWordCalculator = episodeFilePreferredWordCalculator; _logger = logger; } @@ -148,7 +150,7 @@ namespace NzbDrone.Core.History SeriesId = episode.SeriesId, EpisodeId = episode.Id, DownloadId = message.DownloadId, - Language = message.Episode.ParsedEpisodeInfo.Language + Language = message.Episode.ParsedEpisodeInfo.Language, }; history.Data.Add("Indexer", message.Episode.Release.Indexer); @@ -166,6 +168,7 @@ namespace NzbDrone.Core.History history.Data.Add("TvdbId", message.Episode.Release.TvdbId.ToString()); history.Data.Add("TvRageId", message.Episode.Release.TvRageId.ToString()); history.Data.Add("Protocol", ((int)message.Episode.Release.DownloadProtocol).ToString()); + history.Data.Add("PreferredWordScore", message.Episode.PreferredWordScore.ToString()); if (!message.Episode.ParsedEpisodeInfo.ReleaseHash.IsNullOrWhiteSpace()) { @@ -216,6 +219,7 @@ namespace NzbDrone.Core.History history.Data.Add("ImportedPath", Path.Combine(message.EpisodeInfo.Series.Path, message.ImportedEpisode.RelativePath)); history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type); history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name); + history.Data.Add("PreferredWordScore", message.EpisodeInfo.PreferredWordScore.ToString()); _historyRepository.Insert(history); } @@ -258,6 +262,8 @@ namespace NzbDrone.Core.History return; } + var episodeFilePreferredWordScore = _episodeFilePreferredWordCalculator.Calculate(message.EpisodeFile.Series, message.EpisodeFile); + foreach (var episode in message.EpisodeFile.Episodes.Value) { var history = new EpisodeHistory @@ -272,6 +278,7 @@ namespace NzbDrone.Core.History }; history.Data.Add("Reason", message.Reason.ToString()); + history.Data.Add("PreferredWordScore", episodeFilePreferredWordScore.ToString()); _historyRepository.Insert(history); } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs index 0362b3e31..32ab5ffcf 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/AggregationService.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.IO; using NLog; using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators; using NzbDrone.Core.MediaFiles.MediaInfo; +using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation @@ -52,6 +54,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation } localEpisode.Size = _diskProvider.GetFileSize(localEpisode.Path); + localEpisode.SceneName = localEpisode.SceneSource ? SceneNameCalculator.GetSceneName(localEpisode) : null; if (isMediaFile && (!localEpisode.ExistingFile || _configService.EnableMediaInfo)) { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregatePreferredWordScore.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregatePreferredWordScore.cs new file mode 100644 index 000000000..9c1b4d706 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregatePreferredWordScore.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using NzbDrone.Core.Download; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Profiles.Releases; + +namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators +{ + public class AggregatePreferredWordScore : IAggregateLocalEpisode + { + private readonly IPreferredWordService _preferredWordService; + + public AggregatePreferredWordScore(IPreferredWordService preferredWordService) + { + _preferredWordService = preferredWordService; + } + + public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem, bool otherFiles) + { + var series = localEpisode.Series; + var scores = new List(); + + if (localEpisode.FileEpisodeInfo != null) + { + scores.Add(_preferredWordService.Calculate(series, localEpisode.FileEpisodeInfo.ReleaseTitle, 0)); + } + + if (localEpisode.SceneName != null) + { + scores.Add(_preferredWordService.Calculate(series, localEpisode.SceneName, 0)); + } + + localEpisode.PreferredWordScore = scores.MaxOrDefault(); + + return localEpisode; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index 8d5417da1..035ad9921 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -4,11 +4,9 @@ using System.IO; using System.Linq; using NLog; using NzbDrone.Common.Disk; -using NzbDrone.Common.Exceptions; using NzbDrone.Common.Extensions; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; using NzbDrone.Core.Download; @@ -106,7 +104,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport if (newDownload) { episodeFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localEpisode); - episodeFile.SceneName = GetSceneName(downloadClientItem, localEpisode); var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly); oldFiles = moveResult.OldFiles; @@ -196,38 +193,5 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport return Path.GetFileName(path); } - - private string GetSceneName(DownloadClientItem downloadClientItem, LocalEpisode localEpisode) - { - if (downloadClientItem != null) - { - var title = Parser.Parser.RemoveFileExtension(downloadClientItem.Title); - - var parsedTitle = Parser.Parser.ParseTitle(title); - - if (parsedTitle != null && !parsedTitle.FullSeason) - { - return title; - } - } - - var fileName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath()); - - if (SceneChecker.IsSceneTitle(fileName)) - { - return fileName; - } - - var folderTitle = localEpisode.FolderEpisodeInfo?.ReleaseTitle; - - if (localEpisode.FolderEpisodeInfo?.FullSeason == false && - folderTitle.IsNotNullOrWhiteSpace() && - SceneChecker.IsSceneTitle(folderTitle)) - { - return folderTitle; - } - - return null; - } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/SceneNameCalculator.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/SceneNameCalculator.cs new file mode 100644 index 000000000..e91e0b559 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/SceneNameCalculator.cs @@ -0,0 +1,39 @@ +using System.IO; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.EpisodeImport +{ + public static class SceneNameCalculator + { + public static string GetSceneName(LocalEpisode localEpisode) + { + var downloadClientInfo = localEpisode.DownloadClientEpisodeInfo; + + if (downloadClientInfo != null && !downloadClientInfo.FullSeason) + { + return Parser.Parser.RemoveFileExtension(downloadClientInfo.ReleaseTitle); + } + + var fileName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath()); + + if (SceneChecker.IsSceneTitle(fileName)) + { + return fileName; + } + + var folderTitle = localEpisode.FolderEpisodeInfo?.ReleaseTitle; + + if (!otherVideoFiles && + localEpisode.FolderEpisodeInfo?.FullSeason == false && + folderTitle.IsNotNullOrWhiteSpace() && + SceneChecker.IsSceneTitle(folderTitle)) + { + return folderTitle; + } + + return null; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs index 6b6508617..88affb5a1 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/UpgradeSpecification.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using NLog; using NzbDrone.Core.Configuration; using NzbDrone.Core.DecisionEngine; @@ -14,7 +13,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications public class UpgradeSpecification : IImportDecisionEngineSpecification { private readonly IConfigService _configService; - private readonly IPreferredWordService _preferredWordService; private readonly IEpisodeFilePreferredWordCalculator _episodeFilePreferredWordCalculator; private readonly Logger _logger; @@ -24,7 +22,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications Logger logger) { _configService = configService; - _preferredWordService = preferredWordService; _episodeFilePreferredWordCalculator = episodeFilePreferredWordCalculator; _logger = logger; } @@ -34,7 +31,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile); var languageComparer = new LanguageComparer(localEpisode.Series.LanguageProfile); - var preferredWordScore = GetPreferredWordScore(localEpisode); + var preferredWordScore = localEpisode.PreferredWordScore; foreach (var episode in localEpisode.Episodes.Where(e => e.EpisodeFileId > 0)) { @@ -85,28 +82,5 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications return Decision.Accept(); } - - private int GetPreferredWordScore(LocalEpisode localEpisode) - { - var series = localEpisode.Series; - var scores = new List(); - - if (localEpisode.FileEpisodeInfo != null) - { - scores.Add(_preferredWordService.Calculate(series, localEpisode.FileEpisodeInfo.ReleaseTitle, 0)); - } - - if (localEpisode.FolderEpisodeInfo != null) - { - scores.Add(_preferredWordService.Calculate(series, localEpisode.FolderEpisodeInfo.ReleaseTitle, 0)); - } - - if (localEpisode.DownloadClientEpisodeInfo != null) - { - scores.Add(_preferredWordService.Calculate(series, localEpisode.DownloadClientEpisodeInfo.ReleaseTitle, 0)); - } - - return scores.MaxOrDefault(); - } } } diff --git a/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs b/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs index 4e05abec9..8df57e3a6 100644 --- a/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs +++ b/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs @@ -28,7 +28,9 @@ namespace NzbDrone.Core.Parser.Model public bool ExistingFile { get; set; } public bool SceneSource { get; set; } public string ReleaseGroup { get; set; } - + public string SceneName { get; set; } + public int PreferredWordScore { get; set; } + public int SeasonNumber { get