Fixed: Use Nzb title as scene name when available

This commit is contained in:
kayone 2014-09-01 16:37:59 -07:00 committed by Keivan Beigi
parent 17aac81a4d
commit 20287f84ce
13 changed files with 191 additions and 37 deletions

View File

@ -5,6 +5,7 @@ using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.MediaFiles.Events;
@ -24,6 +25,8 @@ namespace NzbDrone.Core.Test.MediaFiles
private List<ImportDecision> _rejectedDecisions;
private List<ImportDecision> _approvedDecisions;
private DownloadClientItem _downloadClientItem;
[SetUp]
public void Setup()
{
@ -38,6 +41,8 @@ namespace NzbDrone.Core.Test.MediaFiles
var episodes = Builder<Episode>.CreateListOfSize(5)
.Build();
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), "Rejected!"));
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), "Rejected!"));
_rejectedDecisions.Add(new ImportDecision(new LocalEpisode(), "Rejected!"));
@ -49,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles
new LocalEpisode
{
Series = series,
Episodes = new List<Episode> {episode},
Episodes = new List<Episode> { episode },
Path = Path.Combine(series.Path, "30 Rock - S01E01 - Pilot.avi"),
Quality = new QualityModel(Quality.Bluray720p),
ParsedEpisodeInfo = new ParsedEpisodeInfo
@ -62,6 +67,8 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IUpgradeMediaFiles>()
.Setup(s => s.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<LocalEpisode>(), false))
.Returns(new EpisodeFileMoveResult());
_downloadClientItem = Builder<DownloadClientItem>.CreateNew().Build();
}
[Test]
@ -106,7 +113,7 @@ namespace NzbDrone.Core.Test.MediaFiles
[Test]
public void should_move_new_downloads()
{
Subject.Import(new List<ImportDecision> {_approvedDecisions.First()}, true);
Subject.Import(new List<ImportDecision> { _approvedDecisions.First() }, true);
Mocker.GetMock<IUpgradeMediaFiles>()
.Verify(v => v.UpgradeEpisodeFile(It.IsAny<EpisodeFile>(), _approvedDecisions.First().LocalEpisode, false),
@ -132,6 +139,52 @@ namespace NzbDrone.Core.Test.MediaFiles
Times.Never());
}
[Test]
public void should_use_nzb_title_as_scene_name()
{
_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)));
}
[Test]
public void should_not_use_nzb_title_as_scene_name_if_full_season()
{
_approvedDecisions.First().LocalEpisode.Path = "c:\\tv\\season1\\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()
{
_approvedDecisions.First().LocalEpisode.Path = "c:\\tv\\malcolm.in.the.middle.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 == "malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot")));
}
[Test]
public void should_not_use_file_name_as_scenename_if_it_doesnt_looks_like_scenename()
{
_approvedDecisions.First().LocalEpisode.Path = "c:\\tv\\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_import_larger_files_first()
{
@ -142,7 +195,7 @@ namespace NzbDrone.Core.Test.MediaFiles
(new LocalEpisode
{
Series = fileDecision.LocalEpisode.Series,
Episodes = new List<Episode> {fileDecision.LocalEpisode.Episodes.First()},
Episodes = new List<Episode> { fileDecision.LocalEpisode.Episodes.First() },
Path = @"C:\Test\TV\30 Rock\30 Rock - S01E01 - Pilot.avi".AsOsAgnostic(),
Quality = new QualityModel(Quality.Bluray720p),
Size = 80.Megabytes()

View File

@ -243,6 +243,7 @@
<Compile Include="ParserTests\SeasonParserFixture.cs" />
<Compile Include="ParserTests\SeriesTitleInfoFixture.cs" />
<Compile Include="ParserTests\SingleEpisodeParserFixture.cs" />
<Compile Include="ParserTests\SceneCheckerFixture.cs" />
<Compile Include="Profiles\ProfileRepositoryFixture.cs" />
<Compile Include="Profiles\ProfileServiceFixture.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -1,11 +1,7 @@
using System;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Expansive;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
using System.Text;

View File

@ -8,21 +8,21 @@ namespace NzbDrone.Core.Test.ParserTests
public class ReleaseGroupParserFixture : CoreTest
{
[TestCase("Castle.2009.S01E14.English.HDTV.XviD-LOL", "LOL")]
[TestCase("Castle 2009 S01E14 English HDTV XviD LOL", "DRONE")]
[TestCase("Acropolis Now S05 EXTRAS DVDRip XviD RUNNER", "DRONE")]
[TestCase("Castle 2009 S01E14 English HDTV XviD LOL", null)]
[TestCase("Acropolis Now S05 EXTRAS DVDRip XviD RUNNER", null)]
[TestCase("Punky.Brewster.S01.EXTRAS.DVDRip.XviD-RUNNER", "RUNNER")]
[TestCase("2020.NZ.2011.12.02.PDTV.XviD-C4TV", "C4TV")]
[TestCase("The.Office.S03E115.DVDRip.XviD-OSiTV", "OSiTV")]
[TestCase("The Office - S01E01 - Pilot [HTDV-480p]", "DRONE")]
[TestCase("The Office - S01E01 - Pilot [HTDV-720p]", "DRONE")]
[TestCase("The Office - S01E01 - Pilot [HTDV-1080p]", "DRONE")]
[TestCase("The Office - S01E01 - Pilot [HTDV-480p]", null)]
[TestCase("The Office - S01E01 - Pilot [HTDV-720p]", null)]
[TestCase("The Office - S01E01 - Pilot [HTDV-1080p]", null)]
[TestCase("The.Walking.Dead.S04E13.720p.WEB-DL.AAC2.0.H.264-Cyphanix", "Cyphanix")]
[TestCase("Arrow.S02E01.720p.WEB-DL.DD5.1.H.264.mkv", "DRONE")]
[TestCase("Series Title S01E01 Episode Title", "DRONE")]
[TestCase("The Colbert Report - 2014-06-02 - Thomas Piketty.mkv", "DRONE")]
[TestCase("Real Time with Bill Maher S12E17 May 23, 2014.mp4", "DRONE")]
[TestCase("Reizen Waes - S01E08 - Transistrië, Zuid-Ossetië en Abchazië SDTV.avi", "DRONE")]
[TestCase("Simpsons 10x11 - Wild Barts Cant Be Broken [rl].avi", "DRONE")]
[TestCase("Arrow.S02E01.720p.WEB-DL.DD5.1.H.264.mkv", null)]
[TestCase("Series Title S01E01 Episode Title", null)]
[TestCase("The Colbert Report - 2014-06-02 - Thomas Piketty.mkv", null)]
[TestCase("Real Time with Bill Maher S12E17 May 23, 2014.mp4", null)]
[TestCase("Reizen Waes - S01E08 - Transistrië, Zuid-Ossetië en Abchazië SDTV.avi", null)]
[TestCase("Simpsons 10x11 - Wild Barts Cant Be Broken [rl].avi", null)]
public void should_parse_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);

View File

@ -0,0 +1,37 @@
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class SceneCheckerFixture
{
[TestCase("South.Park.S04E13.Helen.Keller.The.Musical.720p.WEBRip.AAC2.0.H.264-GC")]
[TestCase("Robot.Chicken.S07E02.720p.WEB-DL.DD5.1.H.264-pcsyndicate")]
[TestCase("Archer.2009.S05E06.Baby.Shower.720p.WEB-DL.DD5.1.H.264-iT00NZ")]
[TestCase("30.Rock.S04E17.720p.HDTV.X264-DIMENSION")]
[TestCase("30.Rock.S04.720p.HDTV.X264-DIMENSION")]
public void should_return_true_for_scene_names(string title)
{
SceneChecker.IsSceneTitle(title).Should().BeTrue();
}
[TestCase("S08E05 - Virtual In-Stanity [WEBDL-720p]")]
[TestCase("S08E05 - Virtual In-Stanity.With.Dots [WEBDL-720p]")]
[TestCase("Something")]
[TestCase("86de66b7ef385e2fa56a3e41b98481ea1658bfab")]
[TestCase("30.Rock.S04E17.720p.HDTV.X264", Description = "no group")]
[TestCase("S04E17.720p.HDTV.X264-DIMENSION", Description = "no series title")]
[TestCase("30.Rock.S04E17-DIMENSION", Description = "no quality")]
[TestCase("30.Rock.720p.HDTV.X264-DIMENSION", Description = "no episode")]
public void should_return_false_for_non_scene_names(string title)
{
SceneChecker.IsSceneTitle(title).Should().BeFalse();
}
}
}

View File

@ -0,0 +1,23 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(61)]
public class clear_bad_scene_names : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.Sql("UPDATE [EpisodeFiles] " +
"SET ReleaseGroup = NULL , SceneName = NULL " +
"WHERE " +
" ReleaseGroup IS NULL " +
" OR SceneName IS NULL " +
" OR ReleaseGroup =='DRONE' " +
" OR LENGTH(SceneName) == 40 " +
" OR LENGTH(SceneName) <10 " +
" OR LENGTH(ReleaseGroup) > 20 " +
" OR SceneName NOT LIKE '%.%'");
}
}
}

View File

@ -24,10 +24,5 @@ namespace NzbDrone.Core.MediaFiles
{
return String.Format("[{0}] {1}", Id, RelativePath);
}
//
// public String Path(Series series)
// {
// return System.IO.Path.Combine(series.Path, RelativePath);
// }
}
}

View File

@ -7,6 +7,8 @@ using NzbDrone.Common;
using NzbDrone.Common.Disk;
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;
@ -15,7 +17,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
{
public interface IImportApprovedEpisodes
{
List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem historyItem = null);
List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null);
}
public class ImportApprovedEpisodes : IImportApprovedEpisodes
@ -39,7 +41,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
_logger = logger;
}
public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem historyItem = null)
public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null)
{
var qualifiedImports = decisions.Where(c => c.Approved)
.GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s
@ -80,12 +82,13 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
if (newDownload)
{
bool copyOnly = historyItem != null && historyItem.IsReadOnly;
episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
bool copyOnly = downloadClientItem != null && downloadClientItem.IsReadOnly;
episodeFile.SceneName = GetSceneName(downloadClientItem, localEpisode);
var moveResult = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly);
oldFiles = moveResult.OldFiles;
}
else
{
episodeFile.RelativePath = localEpisode.Series.Path.GetRelativePath(episodeFile.Path);
@ -94,9 +97,9 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
_mediaFileService.Add(episodeFile);
importResults.Add(new ImportResult(importDecision));
if (historyItem != null)
if (downloadClientItem != null)
{
_eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, newDownload, historyItem.DownloadClient, historyItem.DownloadClientId));
_eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile, newDownload, downloadClientItem.DownloadClient, downloadClientItem.DownloadClientId));
}
else
{
@ -121,5 +124,27 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
return importResults;
}
private string GetSceneName(DownloadClientItem downloadClientItem, LocalEpisode localEpisode)
{
if (downloadClientItem != null)
{
var parsedTitle = Parser.Parser.ParseTitle(downloadClientItem.Title);
if (parsedTitle != null && !parsedTitle.FullSeason)
{
return downloadClientItem.Title;
}
}
var fileName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath());
if (SceneChecker.IsSceneTitle(fileName))
{
return fileName;
}
return null;
}
}
}

View File

@ -222,6 +222,7 @@
<Compile Include="Datastore\Migration\057_convert_episode_file_path_to_relative.cs" />
<Compile Include="Datastore\Migration\058_drop_epsiode_file_path.cs" />
<Compile Include="Datastore\Migration\059_add_enable_options_to_indexers.cs" />
<Compile Include="Datastore\Migration\061_clear_bad_scene_names.cs" />
<Compile Include="Datastore\Migration\060_remove_enable_from_indexers.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
@ -662,6 +663,7 @@
<Compile Include="Parser\Model\TorrentInfo.cs" />
<Compile Include="Parser\Parser.cs" />
<Compile Include="Parser\ParsingService.cs" />
<Compile Include="Parser\SceneChecker.cs" />
<Compile Include="Parser\QualityParser.cs" />
<Compile Include="Profiles\GrabDelayMode.cs" />
<Compile Include="Profiles\Profile.cs" />

View File

@ -363,8 +363,8 @@ namespace NzbDrone.Core.Organizer
private void AddEpisodeFileTokens(Dictionary<String, Func<TokenMatch, String>> tokenHandlers, EpisodeFile episodeFile)
{
tokenHandlers["{Original Title}"]= m => episodeFile.SceneName;
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup;
tokenHandlers["{Original Title}"] = m => episodeFile.SceneName;
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? "DRONE";
tokenHandlers["{Quality Title}"] = m => GetQualityTitle(episodeFile.Quality);
}

View File

@ -322,8 +322,6 @@ namespace NzbDrone.Core.Parser
public static string ParseReleaseGroup(string title)
{
const string defaultReleaseGroup = "DRONE";
title = title.Trim();
title = RemoveFileExtension(title);
title = title.TrimEnd("-RP");
@ -337,13 +335,13 @@ namespace NzbDrone.Core.Parser
if (Int32.TryParse(group, out groupIsNumeric))
{
return defaultReleaseGroup;
return null;
}
return group;
}
return defaultReleaseGroup;
return null;
}
public static string RemoveFileExtension(string title)

View File

@ -0,0 +1,23 @@
namespace NzbDrone.Core.Parser
{
public static class SceneChecker
{
//This method should prefer false negatives over false positives.
//It's better not to use a title that might be seen that to use one that isn't scene
public static bool IsSceneTitle(string title)
{
if (!title.Contains(".")) return false;
if (title.Contains(" ")) return false;
var parsedTitle = Parser.ParseTitle(title);
if (parsedTitle == null
|| parsedTitle.ReleaseGroup == null
|| parsedTitle.Quality.Quality == Qualities.Quality.Unknown
|| string.IsNullOrWhiteSpace(parsedTitle.SeriesTitle)) return false;
return true;
}
}
}

View File

@ -11,8 +11,9 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedParameter_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseObjectOrCollectionInitializer/@EntryIndexedValue">HINT</s:String>
<s:Boolean x:Key="/Default/CodeInspection/TestFileAnalysis/SeachForOrphanedProjectFiles/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/TestFileAnalysis/TestClassSuffix/@EntryValue">Fixture</s:String>
<s:String x:Key="/Default/CodeInspection/TestFileAnalysis/TestProjectToCodeProjectNameSpaceRegEx/@EntryValue">^(.*)\.Test(\..*)$</s:String>
<s:String x:Key="/Default/CodeInspection/TestFileAnalysis/TestProjectToCodeProjectNameSpaceRegEx/@EntryValue">^(.*)\.Test$</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=NzbDrone/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="NzbDrone"&gt;&lt;CSArrangeThisQualifier&gt;True&lt;/CSArrangeThisQualifier&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSUseAutoProperty&gt;True&lt;/CSUseAutoProperty&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;CSUseVar&gt;&lt;BehavourStyle&gt;CAN_CHANGE_TO_IMPLICIT&lt;/BehavourStyle&gt;&lt;LocalVariableStyle&gt;IMPLICIT_EXCEPT_SIMPLE_TYPES&lt;/LocalVariableStyle&gt;&lt;ForeachVariableStyle&gt;ALWAYS_IMPLICIT&lt;/ForeachVariableStyle&gt;&lt;/CSUseVar&gt;&lt;CSUpdateFileHeader&gt;True&lt;/CSUpdateFileHeader&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReorderTypeMembers&gt;True&lt;/CSReorderTypeMembers&gt;&lt;XAMLCollapseEmptyTags&gt;False&lt;/XAMLCollapseEmptyTags&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">NzbDrone</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_CHOP_COMPOUND_WHILE_EXPRESSION/@EntryValue">True</s:Boolean>