diff --git a/frontend/src/Settings/MediaManagement/Naming/NamingModal.js b/frontend/src/Settings/MediaManagement/Naming/NamingModal.js
index 9af6a1160..f873ec1d9 100644
--- a/frontend/src/Settings/MediaManagement/Naming/NamingModal.js
+++ b/frontend/src/Settings/MediaManagement/Naming/NamingModal.js
@@ -154,6 +154,10 @@ const otherTokens = [
{ token: '{Custom Format:FormatName}', example: 'AMZN' }
];
+const otherAnimeTokens = [
+ { token: '{Release Hash}', example: 'ABCDEFGH' }
+];
+
const originalTokens = [
{ token: '{Original Title}', example: 'The.Series.Title\'s!.S01E01.WEBDL.1080p.x264-EVOLVE' },
{ token: '{Original Filename}', example: 'the.series.title\'s!.s01e01.webdl.1080p.x264-EVOLVE' }
@@ -535,6 +539,24 @@ class NamingModal extends Component {
}
)
}
+
+ {
+ anime && otherAnimeTokens.map(({ token, example }) => {
+ return (
+
+ );
+ }
+ )
+ }
diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseHashFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseHashFixture.cs
new file mode 100644
index 000000000..e3e8b848c
--- /dev/null
+++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseHashFixture.cs
@@ -0,0 +1,83 @@
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators;
+using NzbDrone.Core.Parser.Model;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Core.Tv;
+using NzbDrone.Test.Common;
+
+namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
+{
+ [TestFixture]
+ public class AggregateReleaseHashFixture : CoreTest
+ {
+ private Series _series;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder.CreateNew().Build();
+ }
+
+ [Test]
+ public void should_prefer_file()
+ {
+ var fileEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 (1280x720 10bit AAC) [ABCDEFGH]");
+ var folderEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 [12345678]");
+ var downloadClientEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 (1280x720 10bit AAC) [ABCD1234]");
+ var localEpisode = new LocalEpisode
+ {
+ FileEpisodeInfo = fileEpisodeInfo,
+ FolderEpisodeInfo = folderEpisodeInfo,
+ DownloadClientEpisodeInfo = downloadClientEpisodeInfo,
+ Path = @"C:\Test\Unsorted TV\Series.Title.S01\Series.Title.S01E01.mkv".AsOsAgnostic(),
+ Series = _series
+ };
+
+ Subject.Aggregate(localEpisode, null);
+
+ localEpisode.ReleaseHash.Should().Be("ABCDEFGH");
+ }
+
+ [Test]
+ public void should_fallback_to_downloadclient()
+ {
+ var fileEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 (1280x720 10bit AAC)");
+ var downloadClientEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 (1280x720 10bit AAC) [ABCD1234]");
+ var folderEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 [12345678]");
+ var localEpisode = new LocalEpisode
+ {
+ FileEpisodeInfo = fileEpisodeInfo,
+ FolderEpisodeInfo = folderEpisodeInfo,
+ DownloadClientEpisodeInfo = downloadClientEpisodeInfo,
+ Path = @"C:\Test\Unsorted TV\Series.Title.S01\Series.Title.S01E01.WEB-DL.mkv".AsOsAgnostic(),
+ Series = _series
+ };
+
+ Subject.Aggregate(localEpisode, null);
+
+ localEpisode.ReleaseHash.Should().Be("ABCD1234");
+ }
+
+ [Test]
+ public void should_fallback_to_folder()
+ {
+ var fileEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 (1280x720 10bit AAC)");
+ var downloadClientEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 (1280x720 10bit AAC)");
+ var folderEpisodeInfo = Parser.Parser.ParseTitle("[DHD] Series Title! - 08 [12345678]");
+ var localEpisode = new LocalEpisode
+ {
+ FileEpisodeInfo = fileEpisodeInfo,
+ FolderEpisodeInfo = folderEpisodeInfo,
+ DownloadClientEpisodeInfo = downloadClientEpisodeInfo,
+ Path = @"C:\Test\Unsorted TV\Series.Title.S01\Series.Title.S01E01.WEB-DL.mkv".AsOsAgnostic(),
+ Series = _series
+ };
+
+ Subject.Aggregate(localEpisode, null);
+
+ localEpisode.ReleaseHash.Should().Be("12345678");
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
index 521d2d8d9..3b0cdb0af 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs
@@ -991,6 +991,28 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
result.Should().EndWith("HDR");
}
+ [Test]
+ public void should_replace_release_hash_with_stored_hash()
+ {
+ _namingConfig.StandardEpisodeFormat = "{Release Hash}";
+
+ _episodeFile.ReleaseHash = "ABCDEFGH";
+
+ Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile)
+ .Should().Be("ABCDEFGH");
+ }
+
+ [Test]
+ public void should_replace_null_release_hash_with_empty_string()
+ {
+ _namingConfig.StandardEpisodeFormat = "{Release Hash}";
+
+ _episodeFile.ReleaseHash = null;
+
+ Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile)
+ .Should().Be(string.Empty);
+ }
+
private void GivenMediaInfoModel(string videoCodec = "h264",
string audioCodec = "dts",
int audioChannels = 6,
diff --git a/src/NzbDrone.Core.Test/ParserTests/AnimeMetadataParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/AnimeMetadataParserFixture.cs
index 5a1f8bef4..52794f643 100644
--- a/src/NzbDrone.Core.Test/ParserTests/AnimeMetadataParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/AnimeMetadataParserFixture.cs
@@ -22,12 +22,42 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Series Title - 031 - The Resolution to Kill [Lunar].avi", "Lunar", "")]
[TestCase("[ACX]Series Title 01 Episode Name [Kosaka] [9C57891E].mkv", "ACX", "9C57891E")]
[TestCase("[S-T-D] Series Title! - 06 (1280x720 10bit AAC) [59B3F2EA].mkv", "S-T-D", "59B3F2EA")]
- public void should_parse_absolute_numbers(string postTitle, string subGroup, string hash)
+
+ // These tests are dupes of the above, except with parenthesized hashes instead of square bracket
+ [TestCase("[SubDESU]_Show_Title_DxD_07_(1280x720_x264-AAC)_(6B7FD717)", "SubDESU", "6B7FD717")]
+ [TestCase("[Chihiro]_Show_Title!!_-_06_[848x480_H.264_AAC](859EEAFA)", "Chihiro", "859EEAFA")]
+ [TestCase("[Underwater]_Show_Title_-_12_(720p)_(5C7BC4F9)", "Underwater", "5C7BC4F9")]
+ [TestCase("[HorribleSubs]_Show_Title_-_33_[720p]", "HorribleSubs", "")]
+ [TestCase("[HorribleSubs] Show-Title - 13 [1080p].mkv", "HorribleSubs", "")]
+ [TestCase("[Doremi].Show.Title.5.Go.Go!.31.[1280x720].(C65D4B1F).mkv", "Doremi", "C65D4B1F")]
+ [TestCase("[Doremi].Show.Title.5.Go.Go!.31[1280x720].(C65D4B1F)", "Doremi", "C65D4B1F")]
+ [TestCase("[Doremi].Show.Title.5.Go.Go!.31.[1280x720].mkv", "Doremi", "")]
+ [TestCase("[K-F] Series Title 214", "K-F", "")]
+ [TestCase("[K-F] Series Title S10E14 214", "K-F", "")]
+ [TestCase("[K-F] Series Title 10x14 214", "K-F", "")]
+ [TestCase("[K-F] Series Title 214 10x14", "K-F", "")]
+ [TestCase("Series Title - 031 - The Resolution to Kill [Lunar].avi", "Lunar", "")]
+ [TestCase("[ACX]Series Title 01 Episode Name [Kosaka] (9C57891E).mkv", "ACX", "9C57891E")]
+ [TestCase("[S-T-D] Series Title! - 06 (1280x720 10bit AAC) (59B3F2EA).mkv", "S-T-D", "59B3F2EA")]
+ public void should_parse_releasegroup_and_hash(string postTitle, string subGroup, string hash)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Should().NotBeNull();
result.ReleaseGroup.Should().Be(subGroup);
result.ReleaseHash.Should().Be(hash);
}
+
+ [TestCase("[DHD] Series Title! - 08 (1280x720 10bit AAC) [8B00F2EA].mkv", "8B00F2EA")]
+ [TestCase("[DHD] Series Title! - 10 (1280x720 10bit AAC) [10BBF2EA].mkv", "10BBF2EA")]
+ [TestCase("[DHD] Series Title! - 08 (1280x720 10bit AAC) [008BF28B].mkv", "008BF28B")]
+ [TestCase("[DHD] Series Title! - 10 (1280x720 10bit AAC) [000BF10B].mkv", "000BF10B")]
+ [TestCase("[DHD] Series Title! - 08 (1280x720 8bit AAC) [8B8BF2EA].mkv", "8B8BF2EA")]
+ [TestCase("[DHD] Series Title! - 10 (1280x720 8bit AAC) [10B10BEA].mkv", "10B10BEA")]
+ public void should_parse_release_hashes_with_10b_or_8b(string postTitle, string hash)
+ {
+ var result = Parser.Parser.ParseTitle(postTitle);
+ result.Should().NotBeNull();
+ result.ReleaseHash.Should().Be(hash);
+ }
}
}
diff --git a/src/NzbDrone.Core/Datastore/Migration/204_add_release_hash.cs b/src/NzbDrone.Core/Datastore/Migration/204_add_release_hash.cs
new file mode 100644
index 000000000..887d35cda
--- /dev/null
+++ b/src/NzbDrone.Core/Datastore/Migration/204_add_release_hash.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using Dapper;
+using FluentMigrator;
+using NzbDrone.Common.Extensions;
+using NzbDrone.Core.Datastore.Migration.Framework;
+using NzbDrone.Core.Parser.Model;
+
+namespace NzbDrone.Core.Datastore.Migration
+{
+ [Migration(204)]
+ public class add_add_release_hash : NzbDroneMigrationBase
+ {
+ protected override void MainDbUpgrade()
+ {
+ Alter.Table("EpisodeFiles").AddColumn("ReleaseHash").AsString().Nullable();
+
+ Execute.WithConnection(UpdateEpisodeFiles);
+ }
+
+ private void UpdateEpisodeFiles(IDbConnection conn, IDbTransaction tran)
+ {
+ var updates = new List