Skip to content

Commit 7599cbe

Browse files
committed
Add file name replace option - resolve #18
1 parent 46e85f6 commit 7599cbe

8 files changed

Lines changed: 83 additions & 14 deletions

File tree

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"preLaunchTask": "build",
1212
// If you have changed target frameworks, make sure to update the program path.
1313
"program": "${workspaceFolder}/autotag.cli/bin/Debug/net7.0/autotag.dll",
14-
"args": ["/home/james/Desktop/test", "/home/james/Desktop/Inside No, 9 S02E02.mkv", "-v"],
14+
"args": ["--replace a b --print-config"],
1515
"cwd": "${workspaceFolder}/autotag.cli",
1616
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
1717
"console": "internalConsole",

AutoTag.CLI/AutoTag.CLI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<AssemblyName>autotag</AssemblyName>
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net7.0</TargetFramework>
6-
<Version>3.5.2</Version>
6+
<Version>3.5.3</Version>
77
<Nullable>enable</Nullable>
88
<ImplicitUsings>enable</ImplicitUsings>
99
</PropertyGroup>

AutoTag.CLI/Options/OptionsBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace AutoTag.CLI.Options;
66
public abstract class OptionsBase<TOptions>
77
{
88
private static Dictionary<string, Option> _propertyOptions = new Dictionary<string, Option>();
9-
protected static Option<TValue> GetOption<TValue>(Expression<Func<TOptions, TValue>> property)
9+
protected static Option<TValue> GetOption<TValue>(Expression<Func<TOptions, TValue>> property, Action<Option<TValue>>? configure = null)
1010
{
1111
var info = GetPropertyInfo(property);
1212
var attr = info.GetCustomAttribute(typeof(CommandLineOptionAttribute<TValue>)) as CommandLineOptionAttribute<TValue>;
@@ -15,6 +15,8 @@ protected static Option<TValue> GetOption<TValue>(Expression<Func<TOptions, TVal
1515
var option = attr.GetOption();
1616
_propertyOptions[info.Name] = option;
1717

18+
configure?.Invoke(option);
19+
1820
return option;
1921
}
2022
else

AutoTag.CLI/Options/RenameOptions.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,25 @@ public class RenameOptions : OptionsBase<RenameOptions>, IOptionsBase<RenameOpti
1616
[CommandLineOption<bool?>("--rename-subs", "Rename subtitle files")]
1717
public bool? RenameSubs { get; set; }
1818

19+
[CommandLineOption<IEnumerable<string>>("--replace", "Replace <replace> with <replacement> in file names")]
20+
public IEnumerable<string>? FileNameReplaces { get; set; }
21+
1922
public static IEnumerable<Option> GetOptions()
2023
{
2124
yield return GetOption(o => o.NoRename);
2225
yield return GetOption(o => o.TVPattern);
2326
yield return GetOption(o => o.MoviePattern);
2427
yield return GetOption(o => o.WindowsSafe);
2528
yield return GetOption(o => o.RenameSubs);
29+
yield return GetOption(
30+
o => o.FileNameReplaces,
31+
o =>
32+
{
33+
o.AllowMultipleArgumentsPerToken = true;
34+
o.Arity = new ArgumentArity(2, 2);
35+
o.ArgumentHelpName = "replace replacement";
36+
}
37+
);
2638
}
2739

2840
public static RenameOptions GetBoundValues(BindingContext context) =>
@@ -32,7 +44,8 @@ public static RenameOptions GetBoundValues(BindingContext context) =>
3244
TVPattern = GetValueForProperty(o => o.TVPattern, context),
3345
MoviePattern = GetValueForProperty(o => o.MoviePattern, context),
3446
WindowsSafe = GetValueForProperty(o => o.WindowsSafe, context),
35-
RenameSubs = GetValueForProperty(o => o.RenameSubs, context)
47+
RenameSubs = GetValueForProperty(o => o.RenameSubs, context),
48+
FileNameReplaces = GetValueForProperty(o => o.FileNameReplaces, context),
3649
};
3750

3851
public void UpdateConfig(AutoTagConfig config)
@@ -61,5 +74,10 @@ public void UpdateConfig(AutoTagConfig config)
6174
{
6275
config.RenameSubtitles = RenameSubs.Value;
6376
}
77+
78+
if (FileNameReplaces != null && FileNameReplaces.Any())
79+
{
80+
config.FileNameReplaces = FileNameReplace.FromStrings(FileNameReplaces);
81+
}
6482
}
6583
}

AutoTag.Core/AutoTagConfig.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace AutoTag.Core;
22
public class AutoTagConfig
33
{
44
public enum Modes { TV, Movie };
5-
public const int CurrentVer = 8;
5+
public const int CurrentVer = 9;
66
public int ConfigVer { get; set; } = CurrentVer;
77
public Modes Mode { get; set; } = Modes.TV;
88
public bool ManualMode { get; set; } = false;
@@ -18,6 +18,7 @@ public enum Modes { TV, Movie };
1818
public bool AppleTagging { get; set; } = false;
1919
public bool RenameSubtitles { get; set; } = false;
2020
public string Language { get; set; } = "en";
21+
public IEnumerable<FileNameReplace> FileNameReplaces { get; set; } = Enumerable.Empty<FileNameReplace>();
2122

2223
public bool IsTVMode() => Mode == Modes.TV;
2324
}

AutoTag.Core/FileNameReplace.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace AutoTag.Core;
2+
3+
public class FileNameReplace
4+
{
5+
public required string Replace { get; set; }
6+
public required string Replacement { get; set; }
7+
8+
public string Apply(string str) => str.Replace(Replace, Replacement);
9+
10+
public static IEnumerable<FileNameReplace> FromStrings(IEnumerable<string> strings)
11+
{
12+
if (strings.Count() % 2 != 0)
13+
{
14+
throw new ArgumentException("Collection must have an even number of elements", nameof(strings));
15+
}
16+
17+
string? previous = null;
18+
foreach (string str in strings)
19+
{
20+
if (previous == null)
21+
{
22+
previous = str;
23+
}
24+
else
25+
{
26+
yield return new FileNameReplace
27+
{
28+
Replace = previous,
29+
Replacement = str
30+
};
31+
32+
previous = null;
33+
}
34+
}
35+
}
36+
}

AutoTag.Core/FileWriter.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,11 @@ private static bool RenameFile(string path, string newName, Action<string> setPa
115115
bool fileSuccess = true;
116116
string newPath = Path.Combine(
117117
Path.GetDirectoryName(path)!,
118-
EscapeFilename(
118+
GetFileName(
119119
newName,
120120
Path.GetFileNameWithoutExtension(path),
121-
setStatus
121+
setStatus,
122+
config
122123
)
123124
+ Path.GetExtension(path)
124125
);
@@ -156,15 +157,21 @@ private static bool RenameFile(string path, string newName, Action<string> setPa
156157
return fileSuccess;
157158
}
158159

159-
private static string EscapeFilename(string filename, string oldFilename, Action<string, MessageType> setStatus)
160+
private static string GetFileName(string fileName, string oldFileName, Action<string, MessageType> setStatus, AutoTagConfig config)
160161
{
161-
string result = String.Join("", filename.Split(invalidFilenameChars));
162-
if (result != oldFilename && result.Length != filename.Length)
162+
string result = fileName;
163+
foreach (var replace in config.FileNameReplaces)
164+
{
165+
result = replace.Apply(result);
166+
}
167+
168+
string escapedResult = String.Concat(result.Split(invalidFilenameChars));
169+
if (escapedResult != oldFileName && escapedResult.Length != fileName.Length)
163170
{
164171
setStatus("Warning: Invalid characters in file name, automatically removing", MessageType.Warning);
165172
}
166173

167-
return result;
174+
return escapedResult;
168175
}
169176

170177
private static char[]? invalidFilenameChars { get; set; }

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Options:
4040
--movie-pattern <movie-pattern> Rename pattern for movies
4141
--windows-safe Remove invalid Windows file name characters when renaming
4242
--rename-subs Rename subtitle files
43+
--replace <replace replacement> Replace <replace> with <replacement> in file names
4344
-c, --config <config> Config file path
4445
-p, --pattern <pattern> Custom regex to parse TV episode information
4546
-v, --verbose Enable verbose output mode
@@ -71,7 +72,10 @@ The regex pattern should have 3 named capturing groups: `SeriesName`, `Season` a
7172
Note that on Windows all directory separators (`\`) must be escaped as `\\`.
7273

7374
### Windows Safe
74-
The `--windows-safe` option is for use on Linux where the files written may be accessed by a Windows host, or are being written to an NTFS filesystem.
75+
The `--windows-safe` option is for use on Linux/macOS where the files written may be accessed by a Windows host, or are being written to an NTFS filesystem. It automatically removes any invalid NTFS file name characters.
76+
77+
### File Name Replacements
78+
The `--replace` option allows specific characters or strings in a file name to be replaced, e.g. `--replace a b` will replace all the `a` characters in the file name with `b`. This option can be used multiple times for multiple replacements, e.g. `--replace a b --replace foo bar --replace c ''`. **Note: the arguments for this option are case sensitive.**
7579

7680
### Extended Tagging
7781
The `--extended-tagging` option adds additional information to Matroska video files such as actors and their characters. This option is not enabled by default because it may reduce tagging speed significantly due to the additional API requests needed.
@@ -82,7 +86,7 @@ The language of the metadata can be set using the `-l` or `--language` option. T
8286
## Config
8387
AutoTag creates a config file to store default preferences at `~/.config/autotag/conf.json` or `%APPDATA%\Roaming\autotag\conf.json`. A different config file can be specified using the `-c` option. If the file does not exist, a file will be created with the default settings:
8488
```
85-
"configVer": 8, // Internal use
89+
"configVer": 9, // Internal use
8690
"mode": 0, // Default tagging mode, 0 = TV, 1 = Movie
8791
"manualMode": false, // Manual tagging mode
8892
"verbose": false, // Verbose output
@@ -96,7 +100,8 @@ AutoTag creates a config file to store default preferences at `~/.config/autotag
96100
"extendedTagging": false, // Add more information to Matroska file tags
97101
"appleTagging": false, // Add extra tags to mp4 files for use with Apple devices and software
98102
"renameSubtitles": false, // Rename subtitle files
99-
"language": "en" // Metadata language
103+
"language": "en" // Metadata language,
104+
"fileNameReplaces": [] // File name character replacements. Array of objects of the form { "replace": "", replacement: "" }
100105
```
101106

102107
## Moving away from TheTVDB

0 commit comments

Comments
 (0)