mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-03-18 16:24:13 -04:00
Compare commits
8 Commits
v3
...
3.0.10.156
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67e2fe551a | ||
|
|
5db5b1dace | ||
|
|
c7919f80de | ||
|
|
1a1d427c42 | ||
|
|
9263fc1564 | ||
|
|
8ab040f612 | ||
|
|
d6dff451e0 | ||
|
|
ac7afc351c |
@@ -45,6 +45,7 @@ function EditIndexerModalContent(props) {
|
||||
tags,
|
||||
fields,
|
||||
priority,
|
||||
seasonSearchMaximumSingleEpisodeAge,
|
||||
protocol,
|
||||
downloadClientId
|
||||
} = item;
|
||||
@@ -153,6 +154,23 @@ function EditIndexerModalContent(props) {
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>Maximum Single Episode Age</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="seasonSearchMaximumSingleEpisodeAge"
|
||||
helpText="During a full season search only season packs will be allowed when the season's last episode is older than this setting. Standard series only. Use 0 to disable."
|
||||
min={0}
|
||||
unit="days"
|
||||
{...seasonSearchMaximumSingleEpisodeAge}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
|
||||
@@ -265,6 +265,11 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
protected virtual void MoveFileInternal(string source, string destination)
|
||||
{
|
||||
if (File.Exists(destination))
|
||||
{
|
||||
throw new FileAlreadyExistsException("File already exists", destination);
|
||||
}
|
||||
|
||||
File.Move(source, destination);
|
||||
}
|
||||
|
||||
|
||||
14
src/NzbDrone.Common/Disk/FileAlreadyExistsException.cs
Normal file
14
src/NzbDrone.Common/Disk/FileAlreadyExistsException.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
{
|
||||
public class FileAlreadyExistsException : Exception
|
||||
{
|
||||
public string Filename { get; set; }
|
||||
|
||||
public FileAlreadyExistsException(string message, string filename) : base(message)
|
||||
{
|
||||
Filename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using FizzWare.NBuilder;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SingleEpisodeAgeDownloadDecisionFixture : CoreTest<SeasonPackOnlySpecification>
|
||||
{
|
||||
private RemoteEpisode parseResultMulti;
|
||||
private RemoteEpisode parseResultSingle;
|
||||
private Series series;
|
||||
private List<Episode> episodes;
|
||||
private SeasonSearchCriteria multiSearch;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Seasons = Builder<Season>.CreateListOfSize(1).Build().ToList())
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.Build();
|
||||
|
||||
episodes = new List<Episode>();
|
||||
episodes.Add(CreateEpisodeStub(1, 400));
|
||||
episodes.Add(CreateEpisodeStub(2, 370));
|
||||
episodes.Add(CreateEpisodeStub(3, 340));
|
||||
episodes.Add(CreateEpisodeStub(4, 310));
|
||||
|
||||
multiSearch = new SeasonSearchCriteria();
|
||||
multiSearch.Episodes = episodes.ToList();
|
||||
multiSearch.SeasonNumber = 1;
|
||||
|
||||
parseResultMulti = new RemoteEpisode
|
||||
{
|
||||
Series = series,
|
||||
Release = new ReleaseInfo(),
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)), FullSeason = true },
|
||||
Episodes = episodes.ToList()
|
||||
};
|
||||
|
||||
parseResultSingle = new RemoteEpisode
|
||||
{
|
||||
Series = series,
|
||||
Release = new ReleaseInfo(),
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
|
||||
Episodes = new List<Episode>()
|
||||
};
|
||||
}
|
||||
|
||||
Episode CreateEpisodeStub(int number, int age)
|
||||
{
|
||||
return new Episode() {
|
||||
SeasonNumber = 1,
|
||||
EpisodeNumber = number,
|
||||
AirDateUtc = DateTime.UtcNow.AddDays(-age)
|
||||
};
|
||||
}
|
||||
|
||||
[TestCase(1, 200, false)]
|
||||
[TestCase(4, 200, false)]
|
||||
[TestCase(1, 600, true)]
|
||||
[TestCase(1, 365, true)]
|
||||
[TestCase(4, 365, true)]
|
||||
[TestCase(1, 0, true)]
|
||||
public void single_episode_release(int episode, int SeasonSearchMaximumSingleEpisodeAge, bool expectedResult)
|
||||
{
|
||||
parseResultSingle.Release.SeasonSearchMaximumSingleEpisodeAge = SeasonSearchMaximumSingleEpisodeAge;
|
||||
parseResultSingle.Episodes.Clear();
|
||||
parseResultSingle.Episodes.Add(episodes.Find(e => e.EpisodeNumber == episode));
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, multiSearch).Accepted.Should().Be(expectedResult);
|
||||
}
|
||||
|
||||
// should always accept all season packs
|
||||
[TestCase(200, true)]
|
||||
[TestCase(600, true)]
|
||||
[TestCase(365, true)]
|
||||
[TestCase(0, true)]
|
||||
public void multi_episode_release(int SeasonSearchMaximumSingleEpisodeAge, bool expectedResult)
|
||||
{
|
||||
parseResultMulti.Release.SeasonSearchMaximumSingleEpisodeAge = SeasonSearchMaximumSingleEpisodeAge;
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultMulti, multiSearch).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,8 +82,8 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
|
||||
[TestCase("Series Title - S01E01.srt", "Series Title - S01E01.srt")]
|
||||
[TestCase("Series.Title.S01E01.en.srt", "Series Title - S01E01.en.srt")]
|
||||
[TestCase("Series.Title.S01E01.english.srt", "Series Title - S01E01.en.srt")]
|
||||
[TestCase("Series-Title-S01E01-fr-cc.srt", "Series Title - S01E01.fr.srt")]
|
||||
[TestCase("Series Title S01E01_en_sdh_forced.srt", "Series Title - S01E01.en.srt")]
|
||||
[TestCase("Series-Title-S01E01-fr-cc.srt", "Series Title - S01E01.fr.cc.srt")]
|
||||
[TestCase("Series Title S01E01_en_sdh_forced.srt", "Series Title - S01E01.en.sdh.forced.srt")]
|
||||
[TestCase("Series_Title_S01E01 en.srt", "Series Title - S01E01.en.srt")]
|
||||
[TestCase(@"Subs\S01E01.en.srt", "Series Title - S01E01.en.srt")]
|
||||
[TestCase(@"Subs\Series.Title.S01E01\2_en.srt", "Series Title - S01E01.en.srt")]
|
||||
@@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
|
||||
var files = new List<string>
|
||||
{
|
||||
Path.Combine(_episodeFolder, "Series.Title.S01E01.en.srt").AsOsAgnostic(),
|
||||
Path.Combine(_episodeFolder, "Series.Title.S01E01.english.srt").AsOsAgnostic(),
|
||||
Path.Combine(_episodeFolder, "Series.Title.S01E01.eng.srt").AsOsAgnostic(),
|
||||
Path.Combine(_episodeFolder, "Subs", "Series_Title_S01E01_en_forced.srt").AsOsAgnostic(),
|
||||
Path.Combine(_episodeFolder, "Subs", "Series.Title.S01E01", "2_fr.srt").AsOsAgnostic()
|
||||
};
|
||||
@@ -113,7 +113,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
|
||||
{
|
||||
"Series Title - S01E01.1.en.srt",
|
||||
"Series Title - S01E01.2.en.srt",
|
||||
"Series Title - S01E01.3.en.srt",
|
||||
"Series Title - S01E01.en.forced.srt",
|
||||
"Series Title - S01E01.fr.srt",
|
||||
};
|
||||
|
||||
@@ -126,6 +126,35 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
|
||||
results[i].RelativePath.AsOsAgnostic().PathEquals(Path.Combine("Season 1", expectedOutputs[i]).AsOsAgnostic()).Should().Be(true);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_import_multiple_subtitle_files_per_language_with_tags()
|
||||
{
|
||||
var files = new List<string>
|
||||
{
|
||||
Path.Combine(_episodeFolder, "Series.Title.S01E01.en.forced.cc.srt").AsOsAgnostic(),
|
||||
Path.Combine(_episodeFolder, "Series.Title.S01E01.other.en.forced.cc.srt").AsOsAgnostic(),
|
||||
Path.Combine(_episodeFolder, "Series.Title.S01E01.en.forced.sdh.srt").AsOsAgnostic(),
|
||||
Path.Combine(_episodeFolder, "Series.Title.S01E01.en.forced.default.srt").AsOsAgnostic(),
|
||||
};
|
||||
|
||||
var expectedOutputs = new[]
|
||||
{
|
||||
"Series Title - S01E01.1.en.forced.cc.srt",
|
||||
"Series Title - S01E01.2.en.forced.cc.srt",
|
||||
"Series Title - S01E01.en.forced.sdh.srt",
|
||||
"Series Title - S01E01.en.forced.default.srt"
|
||||
};
|
||||
|
||||
var results = Subject.ImportFiles(_localEpisode, _episodeFile, files, true).ToList();
|
||||
|
||||
results.Count().Should().Be(expectedOutputs.Length);
|
||||
|
||||
for (int i = 0; i < expectedOutputs.Length; i++)
|
||||
{
|
||||
results[i].RelativePath.AsOsAgnostic().PathEquals(Path.Combine("Season 1", expectedOutputs[i]).AsOsAgnostic()).Should().Be(true);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("sub.srt", "Series Title - S01E01.srt")]
|
||||
|
||||
@@ -256,6 +256,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("The Series 42 S09E13 1.54 GB WEB-RIP 1080p Dual-Audio 2019 MKV", false)]
|
||||
[TestCase("Series.Title.1x04.ITA.1080p.WEBMux.x264-NovaRip", false)]
|
||||
[TestCase("Series.Title.2019.S02E07.Chapter.15.The.Believer.4Kto1080p.DSNYP.Webrip.x265.10bit.EAC3.5.1.Atmos.GokiTAoE", false)]
|
||||
[TestCase("Series.Title.S01.1080p.AMZN.WEB-Rip.DDP5.1.H.264-Telly", false)]
|
||||
public void should_parse_webrip1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.WEBRip1080p, proper);
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
removeFailedDownloads = false;
|
||||
}
|
||||
|
||||
using (var updateClientCmd = conn.CreateCommand(tran, $"UPDATE DownloadClients SET RemoveCompletedDownloads = (CASE WHEN Implementation IN (\"RTorrent\", \"Flood\") THEN 0 ELSE ? END), RemoveFailedDownloads = ?"))
|
||||
using (var updateClientCmd = conn.CreateCommand(tran, $"UPDATE DownloadClients SET RemoveCompletedDownloads = (CASE WHEN Implementation IN ('RTorrent', 'Flood') THEN 0 ELSE ? END), RemoveFailedDownloads = ?"))
|
||||
{
|
||||
updateClientCmd.AddParameter(removeCompletedDownloads ? 1 : 0);
|
||||
updateClientCmd.AddParameter(removeFailedDownloads ? 1 : 0);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(170)]
|
||||
public class add_language_tags_to_subtitle_files : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("SubtitleFiles").AddColumn("LanguageTags").AsString().Nullable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(172)]
|
||||
public class add_SeasonSearchMaximumSingleEpisodeAge_to_indexers : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Indexers").AddColumn("SeasonSearchMaximumSingleEpisodeAge").AsInt32().NotNullable().WithDefaultValue(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,11 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
|
||||
public override bool SupportsTransactions => true;
|
||||
|
||||
public override bool TableExists(string schemaName, string tableName)
|
||||
{
|
||||
return Exists("select count(*) from sqlite_master where name='{0}' and type='table'", tableName);
|
||||
}
|
||||
|
||||
public override void Process(AlterColumnExpression expression)
|
||||
{
|
||||
var tableDefinition = GetTableSchema(expression.TableName);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
{
|
||||
public class SeasonPackOnlySpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SeasonPackOnlySpecification(IConfigService configService, Logger logger)
|
||||
{
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public SpecificationPriority Priority => SpecificationPriority.Default;
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null || searchCriteria.Episodes.Count == 1)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (subject.Release.SeasonSearchMaximumSingleEpisodeAge > 0)
|
||||
{
|
||||
if (subject.Series.SeriesType == SeriesTypes.Standard && !subject.ParsedEpisodeInfo.FullSeason && subject.Episodes.Count >= 1)
|
||||
{
|
||||
// test against episodes of the same season in the current search, and make sure they have an air date
|
||||
var subset = searchCriteria.Episodes.Where(e => e.AirDateUtc.HasValue && e.SeasonNumber == subject.Episodes.First().SeasonNumber).ToList();
|
||||
|
||||
if (subset.Count() > 0 && subset.Max(e => e.AirDateUtc).Value.Before(DateTime.UtcNow - TimeSpan.FromDays(subject.Release.SeasonSearchMaximumSingleEpisodeAge)))
|
||||
{
|
||||
_logger.Debug("Release {0}: last episode in this season aired more than {1} days ago, season pack required.", subject.Release.Title, subject.Release.SeasonSearchMaximumSingleEpisodeAge);
|
||||
return Decision.Reject("Last episode in this season aired more than {0} days ago, season pack required.", subject.Release.SeasonSearchMaximumSingleEpisodeAge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using NzbDrone.Core.Extras.Files;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Extras.Files;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
namespace NzbDrone.Core.Extras.Subtitles
|
||||
@@ -6,5 +7,11 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
public class SubtitleFile : ExtraFile
|
||||
{
|
||||
public Language Language { get; set; }
|
||||
|
||||
public string AggregateString => Language + LanguageTagsAsString + Extension;
|
||||
|
||||
public List<string> LanguageTags { get; set; }
|
||||
|
||||
private string LanguageTagsAsString => string.Join(".", LanguageTags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
foreach (var episodeFile in episodeFiles)
|
||||
{
|
||||
var groupedExtraFilesForEpisodeFile = subtitleFiles.Where(m => m.EpisodeFileId == episodeFile.Id)
|
||||
.GroupBy(s => s.Language + s.Extension).ToList();
|
||||
.GroupBy(s => s.AggregateString).ToList();
|
||||
|
||||
foreach (var group in groupedExtraFilesForEpisodeFile)
|
||||
{
|
||||
@@ -81,7 +81,7 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
|
||||
foreach (var subtitleFile in group)
|
||||
{
|
||||
var suffix = GetSuffix(subtitleFile.Language, copy, groupCount > 1);
|
||||
var suffix = GetSuffix(subtitleFile.Language, copy, subtitleFile.LanguageTags, groupCount > 1);
|
||||
movedFiles.AddIfNotNull(MoveFile(series, episodeFile, subtitleFile, suffix));
|
||||
|
||||
copy++;
|
||||
@@ -116,7 +116,7 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
try
|
||||
{
|
||||
// Filename match
|
||||
if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase))
|
||||
if (Path.GetFileNameWithoutExtension(file).StartsWithIgnoreCase(sourceFileName))
|
||||
{
|
||||
matchingFiles.Add(file);
|
||||
continue;
|
||||
@@ -175,16 +175,24 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
}
|
||||
}
|
||||
|
||||
var subtitleFiles = new List<Tuple<string, Language, string>>();
|
||||
var subtitleFiles = new List<SubtitleFile>();
|
||||
|
||||
foreach (string file in matchingFiles)
|
||||
{
|
||||
var language = LanguageParser.ParseSubtitleLanguage(file);
|
||||
var extension = Path.GetExtension(file);
|
||||
subtitleFiles.Add(new Tuple<string, Language, string>(file, language, extension));
|
||||
var languageTags = LanguageParser.ParseLanguageTags(file);
|
||||
var subFile = new SubtitleFile
|
||||
{
|
||||
Language = language,
|
||||
Extension = extension
|
||||
};
|
||||
subFile.LanguageTags = languageTags.ToList();
|
||||
subFile.RelativePath = PathExtensions.GetRelativePath(sourceFolder, file);
|
||||
subtitleFiles.Add(subFile);
|
||||
}
|
||||
|
||||
var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.Item2 + s.Item3).ToList();
|
||||
var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.AggregateString).ToList();
|
||||
|
||||
foreach (var group in groupedSubtitleFiles)
|
||||
{
|
||||
@@ -193,14 +201,15 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
|
||||
foreach (var file in group)
|
||||
{
|
||||
var path = Path.Combine(sourceFolder, file.RelativePath);
|
||||
var language = file.Language;
|
||||
var extension = file.Extension;
|
||||
var suffix = GetSuffix(language, copy, file.LanguageTags, groupCount > 1);
|
||||
try
|
||||
{
|
||||
var path = file.Item1;
|
||||
var language = file.Item2;
|
||||
var extension = file.Item3;
|
||||
var suffix = GetSuffix(language, copy, groupCount > 1);
|
||||
var subtitleFile = ImportFile(localEpisode.Series, episodeFile, path, isReadOnly, extension, suffix);
|
||||
subtitleFile.Language = language;
|
||||
subtitleFile.LanguageTags = file.LanguageTags;
|
||||
|
||||
_mediaFileAttributeService.SetFilePermissions(path);
|
||||
_subtitleFileService.Upsert(subtitleFile);
|
||||
@@ -211,7 +220,7 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Failed to import subtitle file: {0}", file.Item1);
|
||||
_logger.Warn(ex, "Failed to import subtitle file: {0}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,7 +228,7 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
return importedFiles;
|
||||
}
|
||||
|
||||
private string GetSuffix(Language language, int copy, bool multipleCopies = false)
|
||||
private string GetSuffix(Language language, int copy, List<string> languageTags, bool multipleCopies = false)
|
||||
{
|
||||
var suffixBuilder = new StringBuilder();
|
||||
|
||||
@@ -235,6 +244,12 @@ namespace NzbDrone.Core.Extras.Subtitles
|
||||
suffixBuilder.Append(IsoLanguages.Get(language).TwoLetterCode);
|
||||
}
|
||||
|
||||
if (languageTags.Any())
|
||||
{
|
||||
suffixBuilder.Append(".");
|
||||
suffixBuilder.Append(string.Join(".", languageTags));
|
||||
}
|
||||
|
||||
return suffixBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Indexers
|
||||
public abstract string Name { get; }
|
||||
public abstract DownloadProtocol Protocol { get; }
|
||||
public int Priority { get; set; }
|
||||
public int SeasonSearchMaximumSingleEpisodeAge { get; set; }
|
||||
|
||||
public abstract bool SupportsRss { get; }
|
||||
public abstract bool SupportsSearch { get; }
|
||||
@@ -81,6 +82,7 @@ namespace NzbDrone.Core.Indexers
|
||||
c.Indexer = Definition.Name;
|
||||
c.DownloadProtocol = Protocol;
|
||||
c.IndexerPriority = ((IndexerDefinition)Definition).Priority;
|
||||
c.SeasonSearchMaximumSingleEpisodeAge = ((IndexerDefinition)Definition).SeasonSearchMaximumSingleEpisodeAge;
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace NzbDrone.Core.Indexers
|
||||
public bool SupportsRss { get; set; }
|
||||
public bool SupportsSearch { get; set; }
|
||||
public int Priority { get; set; } = 25;
|
||||
public int SeasonSearchMaximumSingleEpisodeAge { get; set; } = 0;
|
||||
|
||||
public override bool Enable => EnableRss || EnableAutomaticSearch || EnableInteractiveSearch;
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
|
||||
if (qualityCompare < 0)
|
||||
{
|
||||
_logger.Debug("This file isn't a quality upgrade for all episodes. Skipping {0}", localEpisode.Path);
|
||||
return Decision.Reject("Not an upgrade for existing episode file(s)");
|
||||
_logger.Debug("This file isn't a quality upgrade for all episodes. New Quality is {0}. Skipping {1}", localEpisode.Quality.Quality, localEpisode.Path);
|
||||
return Decision.Reject("Not an upgrade for existing episode file(s). New Quality is {0}", localEpisode.Quality.Quality);
|
||||
}
|
||||
|
||||
// Same quality, is not a language upgrade, propers/repacks are preferred and it is not a revision update
|
||||
|
||||
@@ -134,6 +134,10 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
_eventAggregator.PublishEvent(new EpisodeFileRenamedEvent(series, episodeFile, previousPath));
|
||||
}
|
||||
catch (FileAlreadyExistsException ex)
|
||||
{
|
||||
_logger.Warn("File not renamed, there is already a file at the destination: {0}", ex.Filename);
|
||||
}
|
||||
catch (SameFilenameException ex)
|
||||
{
|
||||
_logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
@@ -152,6 +154,25 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
return Language.Unknown;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ParseLanguageTags(string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var simpleFilename = Path.GetFileNameWithoutExtension(fileName);
|
||||
var match = SubtitleLanguageRegex.Match(simpleFilename);
|
||||
var languageTags = match.Groups["tags"].Captures.Cast<Capture>()
|
||||
.Where(tag => !tag.Value.Empty())
|
||||
.Select(tag => tag.Value.ToLower());
|
||||
return languageTags;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Debug(ex, "Failed parsing language tags from subtitle file: {0}", fileName);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
private static Language RegexLanguage(string title)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace NzbDrone.Core.Parser.Model
|
||||
public int IndexerId { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
public int IndexerPriority { get; set; }
|
||||
public int SeasonSearchMaximumSingleEpisodeAge { get; set; }
|
||||
public DownloadProtocol DownloadProtocol { get; set; }
|
||||
public int TvdbId { get; set; }
|
||||
public int TvRageId { get; set; }
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace NzbDrone.Core.Parser
|
||||
|
||||
private static readonly Regex SourceRegex = new Regex(@"\b(?:
|
||||
(?<bluray>BluRay|Blu-Ray|HD-?DVD|BDMux|BD(?!$))|
|
||||
(?<webdl>WEB[-_. ]DL|WEBDL|AmazonHD|iTunesHD|MaxdomeHD|NetflixU?HD|WebHD|[. ]WEB[. ](?:[xh][ .]?26[45]|DDP?5[. ]1)|[. ](?-i:WEB)$|\d+0p(?:[-. ]AMZN)?[-. ]WEB[-. ]|WEB-DLMux|\b\s\/\sWEB\s\/\s\b|(?:AMZN|NF|DP)[. ]WEB[. ])|
|
||||
(?<webdl>WEB[-_. ]DL(?:mux)?|WEBDL|AmazonHD|iTunesHD|MaxdomeHD|NetflixU?HD|WebHD|[. ]WEB[. ](?:[xh][ .]?26[45]|DDP?5[. ]1)|[. ](?-i:WEB)$|(?:720|1080|2160)p[-. ]WEB[-. ]|[-. ]WEB[-. ](?:720|1080|2160)p|\b\s\/\sWEB\s\/\s\b|(?:AMZN|NF|DP)[. -]WEB[. -](?!Rip))|
|
||||
(?<webrip>WebRip|Web-Rip|WEBMux)|
|
||||
(?<hdtv>HDTV)|
|
||||
(?<bdrip>BDRip|BDLight)|
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace NzbDrone.Core.Tv
|
||||
_diskProvider.CreateFolder(new DirectoryInfo(destinationPath).Parent.FullName);
|
||||
_diskTransferService.TransferFolder(sourcePath, destinationPath, TransferMode.Move);
|
||||
|
||||
_logger.ProgressInfo("{0} moved successfully to {1}", series.Title, series.Path);
|
||||
_logger.ProgressInfo("{0} moved successfully to {1}", series.Title, destinationPath);
|
||||
|
||||
_eventAggregator.PublishEvent(new SeriesMovedEvent(series, sourcePath, destinationPath));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Sonarr.Api.V3.Indexers
|
||||
public bool SupportsSearch { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public int SeasonSearchMaximumSingleEpisodeAge { get; set; }
|
||||
public int DownloadClientId { get; set; }
|
||||
}
|
||||
|
||||
@@ -29,6 +30,7 @@ namespace Sonarr.Api.V3.Indexers
|
||||
resource.SupportsSearch = definition.SupportsSearch;
|
||||
resource.Protocol = definition.Protocol;
|
||||
resource.Priority = definition.Priority;
|
||||
resource.SeasonSearchMaximumSingleEpisodeAge = definition.SeasonSearchMaximumSingleEpisodeAge;
|
||||
resource.DownloadClientId = definition.DownloadClientId;
|
||||
|
||||
return resource;
|
||||
@@ -44,6 +46,7 @@ namespace Sonarr.Api.V3.Indexers
|
||||
definition.EnableAutomaticSearch = resource.EnableAutomaticSearch;
|
||||
definition.EnableInteractiveSearch = resource.EnableInteractiveSearch;
|
||||
definition.Priority = resource.Priority;
|
||||
definition.SeasonSearchMaximumSingleEpisodeAge = resource.SeasonSearchMaximumSingleEpisodeAge;
|
||||
definition.DownloadClientId = resource.DownloadClientId;
|
||||
|
||||
return definition;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#! /bin/bash
|
||||
|
||||
# Increment packageVersion when package scripts change
|
||||
packageVersion='3.0.9'
|
||||
packageVersion='3.0.10'
|
||||
|
||||
# For now we keep the build version and package version the same
|
||||
buildVersion=$packageVersion
|
||||
|
||||
Reference in New Issue
Block a user