1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-12-14 11:23:42 +02:00

New: Show preferred word score in history

This commit is contained in:
Mark McDowall 2021-01-30 14:24:33 -08:00
parent d898f55660
commit 8876c9194d
18 changed files with 377 additions and 196 deletions

View File

@ -1,7 +1,8 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge'; 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 Link from 'Components/Link/Link';
import DescriptionList from 'Components/DescriptionList/DescriptionList'; import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem'; import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
@ -22,6 +23,7 @@ function HistoryDetails(props) {
const { const {
indexer, indexer,
releaseGroup, releaseGroup,
preferredWordScore,
nzbInfoUrl, nzbInfoUrl,
downloadClient, downloadClient,
downloadId, downloadId,
@ -56,6 +58,14 @@ function HistoryDetails(props) {
/> />
} }
{
!!preferredWordScore &&
<DescriptionListItem
title="Preferred Word Score"
data={formatPreferredWordScore(preferredWordScore)}
/>
}
{ {
!!nzbInfoUrl && !!nzbInfoUrl &&
<span> <span>
@ -86,7 +96,7 @@ function HistoryDetails(props) {
} }
{ {
!!indexer && !!(age || ageHours || ageMinutes) &&
<DescriptionListItem <DescriptionListItem
title="Age (when grabbed)" title="Age (when grabbed)"
data={formatAge(age, ageHours, ageMinutes)} data={formatAge(age, ageHours, ageMinutes)}
@ -130,6 +140,7 @@ function HistoryDetails(props) {
if (eventType === 'downloadFolderImported') { if (eventType === 'downloadFolderImported') {
const { const {
preferredWordScore,
droppedPath, droppedPath,
importedPath importedPath
} = data; } = data;
@ -159,13 +170,22 @@ function HistoryDetails(props) {
data={importedPath} data={importedPath}
/> />
} }
{
!!preferredWordScore &&
<DescriptionListItem
title="Preferred Word Score"
data={formatPreferredWordScore(preferredWordScore)}
/>
}
</DescriptionList> </DescriptionList>
); );
} }
if (eventType === 'episodeFileDeleted') { if (eventType === 'episodeFileDeleted') {
const { const {
reason reason,
preferredWordScore
} = data; } = data;
let reasonMessage = ''; let reasonMessage = '';
@ -195,6 +215,14 @@ function HistoryDetails(props) {
title="Reason" title="Reason"
data={reasonMessage} data={reasonMessage}
/> />
{
!!preferredWordScore &&
<DescriptionListItem
title="Preferred Word Score"
data={formatPreferredWordScore(preferredWordScore)}
/>
}
</DescriptionList> </DescriptionList>
); );
} }

View File

@ -10,6 +10,12 @@
width: 80px; width: 80px;
} }
.preferredWordScore {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 55px;
}
.releaseGroup { .releaseGroup {
composes: cell from '~Components/Table/Cells/TableRowCell.css'; composes: cell from '~Components/Table/Cells/TableRowCell.css';

View File

@ -1,5 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector'; import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
@ -194,6 +195,17 @@ class HistoryRow extends Component {
); );
} }
if (name === 'preferredWordScore') {
return (
<TableRowCell
key={name}
className={styles.preferredWordScore}
>
{formatPreferredWordScore(data.preferredWordScore)}
</TableRowCell>
);
}
if (name === 'releaseGroup') { if (name === 'releaseGroup') {
return ( return (
<TableRowCell <TableRowCell

View File

@ -191,7 +191,7 @@ class TableOptionsModal extends Component {
<TableOptionsColumnDragSource <TableOptionsColumnDragSource
key={name} key={name}
name={name} name={name}
label={label || columnLabel} label={columnLabel || label}
isVisible={isVisible} isVisible={isVisible}
isModifiable={true} isModifiable={true}
index={index} index={index}
@ -209,7 +209,7 @@ class TableOptionsModal extends Component {
<TableOptionsColumn <TableOptionsColumn
key={name} key={name}
name={name} name={name}
label={label || columnLabel} label={columnLabel || label}
isVisible={isVisible} isVisible={isVisible}
index={index} index={index}
isModifiable={false} isModifiable={false}

View File

@ -3,6 +3,7 @@ import React, { Component } from 'react';
import formatDateTime from 'Utilities/Date/formatDateTime'; import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge'; import formatAge from 'Utilities/Number/formatAge';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
import { icons, kinds, tooltipPositions } from 'Helpers/Props'; import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
@ -193,8 +194,7 @@ class InteractiveSearchRow extends Component {
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.preferredWordScore}> <TableRowCell className={styles.preferredWordScore}>
{preferredWordScore > 0 && `+${preferredWordScore}`} {formatPreferredWordScore(preferredWordScore)}
{preferredWordScore < 0 && preferredWordScore}
</TableRowCell> </TableRowCell>
<TableRowCell className={styles.rejected}> <TableRowCell className={styles.rejected}>

View File

@ -1,7 +1,9 @@
import React from 'react';
import { createAction } from 'redux-actions'; import { createAction } from 'redux-actions';
import createAjaxRequest from 'Utilities/createAjaxRequest'; import createAjaxRequest from 'Utilities/createAjaxRequest';
import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers'; 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 { createThunk, handleThunks } from 'Store/thunks';
import createClearReducer from './Creators/Reducers/createClearReducer'; import createClearReducer from './Creators/Reducers/createClearReducer';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer'; import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
@ -80,6 +82,15 @@ export const defaultState = {
label: 'Release Group', label: 'Release Group',
isVisible: false isVisible: false
}, },
{
name: 'preferredWordScore',
columnLabel: 'Preferred Word Score',
label: React.createElement(Icon, {
name: icons.SCORE,
title: 'Preferred word score'
}),
isVisible: false
},
{ {
name: 'details', name: 'details',
columnLabel: 'Details', columnLabel: 'Details',

View File

@ -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;

View File

@ -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<Series>.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<Episode>.CreateNew()
.Build();
_localEpisode = new LocalEpisode
{
Series = series,
Episodes = new List<Episode> {episode},
Path = Path.Combine(series.Path, "Series Title - S02E23 - Episode Title.mkv"),
Quality = new QualityModel(Quality.Bluray720p),
ReleaseGroup = "DRONE"
};
}
private void GivenExistingFileOnDisk()
{
Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesWithRelativePath(It.IsAny<int>(), It.IsAny<string>()))
.Returns(new List<EpisodeFile>());
}
[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);
}
}
}

View File

@ -21,7 +21,7 @@
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages; using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.MediaFiles namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{ {
[TestFixture] [TestFixture]
public class ImportApprovedEpisodesFixture : CoreTest<ImportApprovedEpisodes> public class ImportApprovedEpisodesFixture : CoreTest<ImportApprovedEpisodes>
@ -169,112 +169,6 @@ public void should_not_move_existing_files()
Times.Never()); 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<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(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<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(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<ImportDecision> { _approvedDecisions.First() }, true, _downloadClientItem);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(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<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(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<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(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<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(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<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(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<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Add(It.Is<EpisodeFile>(c => c.SceneName == null)));
}
[Test] [Test]
public void should_import_larger_files_first() public void should_import_larger_files_first()
{ {

View File

@ -287,14 +287,11 @@ public void should_return_false_if_it_is_not_a_preferred_word_upgrade()
.Setup(s => s.DownloadPropersAndRepacks) .Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotPrefer); .Returns(ProperDownloadTypes.DoNotPrefer);
Mocker.GetMock<IPreferredWordService>()
.Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<string>(), 0))
.Returns(5);
Mocker.GetMock<IEpisodeFilePreferredWordCalculator>() Mocker.GetMock<IEpisodeFilePreferredWordCalculator>()
.Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<EpisodeFile>())) .Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<EpisodeFile>()))
.Returns(10); .Returns(10);
_localEpisode.PreferredWordScore = 5;
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p); _localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1) _localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
@ -364,14 +361,11 @@ public void should_return_true_if_it_is_a_preferred_word_upgrade()
.Setup(s => s.DownloadPropersAndRepacks) .Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotPrefer); .Returns(ProperDownloadTypes.DoNotPrefer);
Mocker.GetMock<IPreferredWordService>()
.Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<string>(), 0))
.Returns(5);
Mocker.GetMock<IEpisodeFilePreferredWordCalculator>() Mocker.GetMock<IEpisodeFilePreferredWordCalculator>()
.Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<EpisodeFile>())) .Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<EpisodeFile>()))
.Returns(1); .Returns(1);
_localEpisode.PreferredWordScore = 5;
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p); _localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1) _localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
@ -397,14 +391,11 @@ public void should_return_true_if_it_has_an_equal_preferred_word_score()
.Setup(s => s.DownloadPropersAndRepacks) .Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotPrefer); .Returns(ProperDownloadTypes.DoNotPrefer);
Mocker.GetMock<IPreferredWordService>()
.Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<string>(), 0))
.Returns(5);
Mocker.GetMock<IEpisodeFilePreferredWordCalculator>() Mocker.GetMock<IEpisodeFilePreferredWordCalculator>()
.Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<EpisodeFile>())) .Setup(s => s.Calculate(It.IsAny<Series>(), It.IsAny<EpisodeFile>()))
.Returns(5); .Returns(5);
_localEpisode.PreferredWordScore = 5;
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p); _localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1) _localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)

View File

@ -26,9 +26,7 @@ public EpisodeHistory()
public EpisodeHistoryEventType EventType { get; set; } public EpisodeHistoryEventType EventType { get; set; }
public Dictionary<string, string> Data { get; set; } public Dictionary<string, string> Data { get; set; }
public Language Language { get; set; } public Language Language { get; set; }
public string DownloadId { get; set; } public string DownloadId { get; set; }
} }
public enum EpisodeHistoryEventType public enum EpisodeHistoryEventType

View File

@ -39,11 +39,13 @@ public class HistoryService : IHistoryService,
IHandle<DownloadIgnoredEvent> IHandle<DownloadIgnoredEvent>
{ {
private readonly IHistoryRepository _historyRepository; private readonly IHistoryRepository _historyRepository;
private readonly IEpisodeFilePreferredWordCalculator _episodeFilePreferredWordCalculator;
private readonly Logger _logger; private readonly Logger _logger;
public HistoryService(IHistoryRepository historyRepository, Logger logger) public HistoryService(IHistoryRepository historyRepository, IEpisodeFilePreferredWordCalculator episodeFilePreferredWordCalculator, Logger logger)
{ {
_historyRepository = historyRepository; _historyRepository = historyRepository;
_episodeFilePreferredWordCalculator = episodeFilePreferredWordCalculator;
_logger = logger; _logger = logger;
} }
@ -148,7 +150,7 @@ public void Handle(EpisodeGrabbedEvent message)
SeriesId = episode.SeriesId, SeriesId = episode.SeriesId,
EpisodeId = episode.Id, EpisodeId = episode.Id,
DownloadId = message.DownloadId, DownloadId = message.DownloadId,
Language = message.Episode.ParsedEpisodeInfo.Language Language = message.Episode.ParsedEpisodeInfo.Language,
}; };
history.Data.Add("Indexer", message.Episode.Release.Indexer); history.Data.Add("Indexer", message.Episode.Release.Indexer);
@ -166,6 +168,7 @@ public void Handle(EpisodeGrabbedEvent message)
history.Data.Add("TvdbId", message.Episode.Release.TvdbId.ToString()); history.Data.Add("TvdbId", message.Episode.Release.TvdbId.ToString());
history.Data.Add("TvRageId", message.Episode.Release.TvRageId.ToString()); history.Data.Add("TvRageId", message.Episode.Release.TvRageId.ToString());
history.Data.Add("Protocol", ((int)message.Episode.Release.DownloadProtocol).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()) if (!message.Episode.ParsedEpisodeInfo.ReleaseHash.IsNullOrWhiteSpace())
{ {
@ -216,6 +219,7 @@ public void Handle(EpisodeImportedEvent message)
history.Data.Add("ImportedPath", Path.Combine(message.EpisodeInfo.Series.Path, message.ImportedEpisode.RelativePath)); history.Data.Add("ImportedPath", Path.Combine(message.EpisodeInfo.Series.Path, message.ImportedEpisode.RelativePath));
history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type); history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type);
history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name); history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name);
history.Data.Add("PreferredWordScore", message.EpisodeInfo.PreferredWordScore.ToString());
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }
@ -258,6 +262,8 @@ public void Handle(EpisodeFileDeletedEvent message)
return; return;
} }
var episodeFilePreferredWordScore = _episodeFilePreferredWordCalculator.Calculate(message.EpisodeFile.Series, message.EpisodeFile);
foreach (var episode in message.EpisodeFile.Episodes.Value) foreach (var episode in message.EpisodeFile.Episodes.Value)
{ {
var history = new EpisodeHistory var history = new EpisodeHistory
@ -272,6 +278,7 @@ public void Handle(EpisodeFileDeletedEvent message)
}; };
history.Data.Add("Reason", message.Reason.ToString()); history.Data.Add("Reason", message.Reason.ToString());
history.Data.Add("PreferredWordScore", episodeFilePreferredWordScore.ToString());
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }

View File

@ -3,10 +3,12 @@
using System.IO; using System.IO;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators;
using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation
@ -52,6 +54,7 @@ public LocalEpisode Augment(LocalEpisode localEpisode, DownloadClientItem downlo
} }
localEpisode.Size = _diskProvider.GetFileSize(localEpisode.Path); localEpisode.Size = _diskProvider.GetFileSize(localEpisode.Path);
localEpisode.SceneName = localEpisode.SceneSource ? SceneNameCalculator.GetSceneName(localEpisode) : null;
if (isMediaFile && (!localEpisode.ExistingFile || _configService.EnableMediaInfo)) if (isMediaFile && (!localEpisode.ExistingFile || _configService.EnableMediaInfo))
{ {

View File

@ -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<int>();
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;
}
}
}

View File

@ -4,11 +4,9 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Exceptions;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
@ -106,7 +104,6 @@ public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownloa
if (newDownload) if (newDownload)
{ {
episodeFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localEpisode); episodeFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localEpisode);
episodeFile.SceneName = GetSceneName(downloadClientItem, localEpisode);
var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly); var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly);
oldFiles = moveResult.OldFiles; oldFiles = moveResult.OldFiles;
@ -196,38 +193,5 @@ private string GetOriginalFilePath(DownloadClientItem downloadClientItem, LocalE
return Path.GetFileName(path); 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;
}
} }
} }

View File

@ -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;
}
}
}

View File

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Linq;
using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
@ -14,7 +13,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
public class UpgradeSpecification : IImportDecisionEngineSpecification public class UpgradeSpecification : IImportDecisionEngineSpecification
{ {
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly IPreferredWordService _preferredWordService;
private readonly IEpisodeFilePreferredWordCalculator _episodeFilePreferredWordCalculator; private readonly IEpisodeFilePreferredWordCalculator _episodeFilePreferredWordCalculator;
private readonly Logger _logger; private readonly Logger _logger;
@ -24,7 +22,6 @@ public UpgradeSpecification(IConfigService configService,
Logger logger) Logger logger)
{ {
_configService = configService; _configService = configService;
_preferredWordService = preferredWordService;
_episodeFilePreferredWordCalculator = episodeFilePreferredWordCalculator; _episodeFilePreferredWordCalculator = episodeFilePreferredWordCalculator;
_logger = logger; _logger = logger;
} }
@ -34,7 +31,7 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down
var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks;
var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile); var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile);
var languageComparer = new LanguageComparer(localEpisode.Series.LanguageProfile); 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)) foreach (var episode in localEpisode.Episodes.Where(e => e.EpisodeFileId > 0))
{ {
@ -85,28 +82,5 @@ public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem down
return Decision.Accept(); return Decision.Accept();
} }
private int GetPreferredWordScore(LocalEpisode localEpisode)
{
var series = localEpisode.Series;
var scores = new List<int>();
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();
}
} }
} }

View File

@ -28,7 +28,9 @@ public LocalEpisode()
public bool ExistingFile { get; set; } public bool ExistingFile { get; set; }
public bool SceneSource { get; set; } public bool SceneSource { get; set; }
public string ReleaseGroup { get; set; } public string ReleaseGroup { get; set; }
public string SceneName { get; set; }
public int PreferredWordScore { get; set; }
public int SeasonNumber public int SeasonNumber
{ {
get get