mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-03-17 21:17:50 +02:00
parent
0c883f7886
commit
293a1bc618
@ -138,7 +138,8 @@ class Naming extends Component {
|
||||
{ key: 1, value: translate('ReplaceWithDash') },
|
||||
{ key: 2, value: translate('ReplaceWithSpaceDash') },
|
||||
{ key: 3, value: translate('ReplaceWithSpaceDashSpace') },
|
||||
{ key: 4, value: translate('SmartReplace'), hint: translate('SmartReplaceHint') }
|
||||
{ key: 4, value: translate('SmartReplace'), hint: translate('SmartReplaceHint') },
|
||||
{ key: 5, value: translate('Custom'), hint: translate('CustomColonReplacementFormatHint') }
|
||||
];
|
||||
|
||||
const standardEpisodeFormatHelpTexts = [];
|
||||
@ -262,6 +263,22 @@ class Naming extends Component {
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
replaceIllegalCharacters && settings.colonReplacementFormat.value === 5 ?
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('ColonReplacement')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="customColonReplacementFormat"
|
||||
helpText={translate('CustomColonReplacementFormatHelpText')}
|
||||
onChange={onInputChange}
|
||||
{...settings.customColonReplacementFormat}
|
||||
/>
|
||||
</FormGroup> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
renameEpisodes &&
|
||||
<div>
|
||||
|
@ -90,5 +90,18 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
||||
.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("Series: Title", ColonReplacementFormat.Custom, "\ua789", "Series\ua789 Title")]
|
||||
[TestCase("Series: Title", ColonReplacementFormat.Custom, "∶", "Series∶ Title")]
|
||||
public void should_replace_colon_with_custom_format(string seriesName, ColonReplacementFormat replacementFormat, string customFormat, string expected)
|
||||
{
|
||||
_series.Title = seriesName;
|
||||
_namingConfig.StandardEpisodeFormat = "{Series Title}";
|
||||
_namingConfig.ColonReplacementFormat = replacementFormat;
|
||||
_namingConfig.CustomColonReplacementFormat = customFormat;
|
||||
|
||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
||||
.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(211)]
|
||||
public class add_custom_colon_replacement_to_naming_config : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("NamingConfig").AddColumn("CustomColonReplacementFormat").AsString().WithDefaultValue("");
|
||||
}
|
||||
}
|
||||
}
|
@ -270,6 +270,9 @@
|
||||
"CreateGroup": "Create Group",
|
||||
"CurrentlyInstalled": "Currently Installed",
|
||||
"Custom": "Custom",
|
||||
"CustomColonReplacement": "Custom Colon Replacement",
|
||||
"CustomColonReplacementFormatHelpText": "Characters to be used as a replacement for colons",
|
||||
"CustomColonReplacementFormatHint": "Valid file system character such as Colon (Letter)",
|
||||
"CustomFilter": "Custom Filter",
|
||||
"CustomFilters": "Custom Filters",
|
||||
"CustomFormat": "Custom Format",
|
||||
|
@ -6,7 +6,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Diacritical;
|
||||
using DryIoc.ImTools;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Disk;
|
||||
@ -112,6 +111,9 @@ namespace NzbDrone.Core.Organizer
|
||||
{ "wel", "cym" }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
public static readonly ImmutableArray<string> BadCharacters = ImmutableArray.Create("\\", "/", "<", ">", "?", "*", "|", "\"");
|
||||
public static readonly ImmutableArray<string> GoodCharacters = ImmutableArray.Create("+", "+", "", "", "!", "-", "", "");
|
||||
|
||||
public FileNameBuilder(INamingConfigService namingConfigService,
|
||||
IQualityDefinitionService qualityDefinitionService,
|
||||
ICacheManager cacheManager,
|
||||
@ -1156,8 +1158,6 @@ namespace NzbDrone.Core.Organizer
|
||||
private static string CleanFileName(string name, NamingConfig namingConfig)
|
||||
{
|
||||
var result = name;
|
||||
string[] badCharacters = { "\\", "/", "<", ">", "?", "*", "|", "\"" };
|
||||
string[] goodCharacters = { "+", "+", "", "", "!", "-", "", "" };
|
||||
|
||||
if (namingConfig.ReplaceIllegalCharacters)
|
||||
{
|
||||
@ -1182,6 +1182,9 @@ namespace NzbDrone.Core.Organizer
|
||||
case ColonReplacementFormat.SpaceDashSpace:
|
||||
replacement = " - ";
|
||||
break;
|
||||
case ColonReplacementFormat.Custom:
|
||||
replacement = namingConfig.CustomColonReplacementFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
result = result.Replace(":", replacement);
|
||||
@ -1192,9 +1195,9 @@ namespace NzbDrone.Core.Organizer
|
||||
result = result.Replace(":", string.Empty);
|
||||
}
|
||||
|
||||
for (var i = 0; i < badCharacters.Length; i++)
|
||||
for (var i = 0; i < BadCharacters.Length; i++)
|
||||
{
|
||||
result = result.Replace(badCharacters[i], namingConfig.ReplaceIllegalCharacters ? goodCharacters[i] : string.Empty);
|
||||
result = result.Replace(BadCharacters[i], namingConfig.ReplaceIllegalCharacters ? GoodCharacters[i] : string.Empty);
|
||||
}
|
||||
|
||||
return result.TrimStart(' ', '.').TrimEnd(' ');
|
||||
@ -1268,6 +1271,7 @@ namespace NzbDrone.Core.Organizer
|
||||
Dash = 1,
|
||||
SpaceDash = 2,
|
||||
SpaceDashSpace = 3,
|
||||
Smart = 4
|
||||
Smart = 4,
|
||||
Custom = 5
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
@ -61,6 +62,13 @@ namespace NzbDrone.Core.Organizer
|
||||
|
||||
return ruleBuilder.SetValidator(new IllegalCharactersValidator());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidCustomColonReplacement<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new IllegalColonCharactersValidator());
|
||||
|
||||
return ruleBuilder.SetValidator(new IllegalCharactersValidator());
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidStandardEpisodeFormatValidator : PropertyValidator
|
||||
@ -132,6 +140,34 @@ namespace NzbDrone.Core.Organizer
|
||||
}
|
||||
|
||||
var invalidCharacters = InvalidPathChars.Where(i => value!.IndexOf(i) >= 0).ToList();
|
||||
|
||||
if (invalidCharacters.Any())
|
||||
{
|
||||
context.MessageFormatter.AppendArgument("InvalidCharacters", string.Join("", invalidCharacters));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class IllegalColonCharactersValidator : PropertyValidator
|
||||
{
|
||||
private static readonly string[] InvalidPathChars = FileNameBuilder.BadCharacters.Concat(new[] { ":" }).ToArray();
|
||||
|
||||
protected override string GetDefaultMessageTemplate() => "Contains illegal characters: {InvalidCharacters}";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var value = context.PropertyValue as string;
|
||||
|
||||
if (value.IsNullOrWhiteSpace())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var invalidCharacters = InvalidPathChars.Where(i => value!.IndexOf(i, StringComparison.Ordinal) >= 0).ToList();
|
||||
|
||||
if (invalidCharacters.Any())
|
||||
{
|
||||
context.MessageFormatter.AppendArgument("InvalidCharacters", string.Join("", invalidCharacters));
|
||||
|
@ -9,6 +9,7 @@ namespace NzbDrone.Core.Organizer
|
||||
RenameEpisodes = false,
|
||||
ReplaceIllegalCharacters = true,
|
||||
ColonReplacementFormat = ColonReplacementFormat.Smart,
|
||||
CustomColonReplacementFormat = string.Empty,
|
||||
MultiEpisodeStyle = MultiEpisodeStyle.PrefixedRange,
|
||||
StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}",
|
||||
DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title} {Quality Full}",
|
||||
@ -21,6 +22,7 @@ namespace NzbDrone.Core.Organizer
|
||||
public bool RenameEpisodes { get; set; }
|
||||
public bool ReplaceIllegalCharacters { get; set; }
|
||||
public ColonReplacementFormat ColonReplacementFormat { get; set; }
|
||||
public string CustomColonReplacementFormat { get; set; }
|
||||
public MultiEpisodeStyle MultiEpisodeStyle { get; set; }
|
||||
public string StandardEpisodeFormat { get; set; }
|
||||
public string DailyEpisodeFormat { get; set; }
|
||||
|
@ -36,6 +36,7 @@ namespace Sonarr.Api.V3.Config
|
||||
SharedValidator.RuleFor(c => c.SeriesFolderFormat).ValidSeriesFolderFormat();
|
||||
SharedValidator.RuleFor(c => c.SeasonFolderFormat).ValidSeasonFolderFormat();
|
||||
SharedValidator.RuleFor(c => c.SpecialsFolderFormat).ValidSpecialsFolderFormat();
|
||||
SharedValidator.RuleFor(c => c.CustomColonReplacementFormat).ValidCustomColonReplacement().When(c => c.ColonReplacementFormat == (int)ColonReplacementFormat.Custom);
|
||||
}
|
||||
|
||||
protected override NamingConfigResource GetResourceById(int id)
|
||||
|
@ -7,6 +7,7 @@ namespace Sonarr.Api.V3.Config
|
||||
public bool RenameEpisodes { get; set; }
|
||||
public bool ReplaceIllegalCharacters { get; set; }
|
||||
public int ColonReplacementFormat { get; set; }
|
||||
public string CustomColonReplacementFormat { get; set; }
|
||||
public int MultiEpisodeStyle { get; set; }
|
||||
public string StandardEpisodeFormat { get; set; }
|
||||
public string DailyEpisodeFormat { get; set; }
|
||||
|
@ -25,6 +25,7 @@ namespace Sonarr.Api.V3.Config
|
||||
RenameEpisodes = model.RenameEpisodes,
|
||||
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
||||
ColonReplacementFormat = (int)model.ColonReplacementFormat,
|
||||
CustomColonReplacementFormat = model.CustomColonReplacementFormat,
|
||||
MultiEpisodeStyle = (int)model.MultiEpisodeStyle,
|
||||
StandardEpisodeFormat = model.StandardEpisodeFormat,
|
||||
DailyEpisodeFormat = model.DailyEpisodeFormat,
|
||||
@ -45,6 +46,7 @@ namespace Sonarr.Api.V3.Config
|
||||
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
|
||||
MultiEpisodeStyle = (MultiEpisodeStyle)resource.MultiEpisodeStyle,
|
||||
ColonReplacementFormat = (ColonReplacementFormat)resource.ColonReplacementFormat,
|
||||
CustomColonReplacementFormat = resource.CustomColonReplacementFormat,
|
||||
StandardEpisodeFormat = resource.StandardEpisodeFormat,
|
||||
DailyEpisodeFormat = resource.DailyEpisodeFormat,
|
||||
AnimeEpisodeFormat = resource.AnimeEpisodeFormat,
|
||||
|
Loading…
x
Reference in New Issue
Block a user