diff --git a/src/.idea/.idea.AudioTagger/.idea/.gitignore b/src/.idea/.idea.AudioTagger/.idea/.gitignore
new file mode 100644
index 0000000..1bcedf4
--- /dev/null
+++ b/src/.idea/.idea.AudioTagger/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/.idea.AudioTagger.iml
+/modules.xml
+/projectSettingsUpdater.xml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/src/.idea/.idea.AudioTagger/.idea/.name b/src/.idea/.idea.AudioTagger/.idea/.name
new file mode 100644
index 0000000..9722465
--- /dev/null
+++ b/src/.idea/.idea.AudioTagger/.idea/.name
@@ -0,0 +1 @@
+AudioTagger
\ No newline at end of file
diff --git a/src/.idea/.idea.AudioTagger/.idea/indexLayout.xml b/src/.idea/.idea.AudioTagger/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/src/.idea/.idea.AudioTagger/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.AudioTagger/.idea/vcs.xml b/src/.idea/.idea.AudioTagger/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/src/.idea/.idea.AudioTagger/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AudioTagger.Console/Operations/MediaFileRenamer.cs b/src/AudioTagger.Console/Operations/MediaFileRenamer.cs
index 67ae3bf..8c7a634 100644
--- a/src/AudioTagger.Console/Operations/MediaFileRenamer.cs
+++ b/src/AudioTagger.Console/Operations/MediaFileRenamer.cs
@@ -60,12 +60,22 @@ public void Start(
return;
}
+ var normalizationForm = settings.Renaming.NormalizationForm.Trim() switch
+ {
+ "D" => NormalizationForm.FormD,
+ "KD" => NormalizationForm.FormKD,
+ "KC" => NormalizationForm.FormKC,
+ _ => NormalizationForm.FormC
+ };
+ printer.Print($"Filename normalization form is {normalizationForm}.");
+
RenameFiles(
eligibleMediaFiles,
workingDirectory,
printer,
settings.Renaming.Patterns,
- settings.Renaming.UseAlbumDirectories);
+ settings.Renaming.UseAlbumDirectories,
+ normalizationForm);
var deletedDirs = DeleteEmptySubDirectories(workingDirectory.FullName, printer);
PrintDeletedDirectories(deletedDirs, printer);
@@ -96,7 +106,8 @@ private static void RenameFiles(
DirectoryInfo workingDirectory,
IPrinter printer,
IEnumerable renamePatterns,
- bool useAlbumDirectories)
+ bool useAlbumDirectories,
+ NormalizationForm normalizationForm)
{
var isCancelRequested = false;
var doConfirm = true;
@@ -106,7 +117,7 @@ private static void RenameFiles(
for (int i = 0; i < mediaFiles.Count; i++)
{
- MediaFile file = mediaFiles.ElementAt(i);
+ var file = mediaFiles.ElementAt(i);
if (file.Title.Length == 0)
{
@@ -131,6 +142,7 @@ private static void RenameFiles(
workingDirectory.FullName,
useArtistDirectory,
useAlbumDirectories,
+ normalizationForm,
ref doConfirm,
renamePatterns);
}
@@ -181,6 +193,7 @@ private static bool RenameSingleFile(
string workingPath,
bool useArtistDirectory,
bool useAlbumDirectory,
+ NormalizationForm normalizationForm,
ref bool doConfirm,
IEnumerable renamePatterns)
{
@@ -207,16 +220,17 @@ private static bool RenameSingleFile(
return false;
}
- MediaFilePathInfo oldPathInfo = new(workingPath, file.FileInfo.FullName);
+ var oldPathInfo = new MediaFilePathInfo(workingPath, file.FileInfo.FullName);
string newArtistDir = useArtistDirectory
- ? GenerateSafeDirectoryName(file)
+ ? GenerateSafeDirectoryName(file).Normalize(normalizationForm)
: string.Empty;
string newAlbumDir = useAlbumDirectory && useArtistDirectory && file.Album.HasText()
- ? IoUtilities.SanitizePath(file.Album)
+ ? IoUtilities.SanitizePath(file.Album).Normalize(normalizationForm)
: string.Empty;
- string newFileName = GenerateFileNameUsingPattern(file, populatedTagNames, matchedRenamePattern);
- MediaFilePathInfo newPathInfo = new(workingPath, [newArtistDir, newAlbumDir], newFileName);
+ string newFileName = GenerateFileName(file, populatedTagNames, matchedRenamePattern)
+ .Normalize(normalizationForm);
+ var newPathInfo = new MediaFilePathInfo(workingPath, [newArtistDir, newAlbumDir], newFileName);
if (oldPathInfo.FullFilePath(true) == newPathInfo.FullFilePath(true))
{
@@ -271,7 +285,7 @@ private static bool RenameSingleFile(
/// Generates and returns a new filename by replacing placeholders within the rename
/// pattern (e.g., `%ALBUM%`) with actual tag data from the `MediaFile`.
///
- static string GenerateFileNameUsingPattern(
+ static string GenerateFileName(
MediaFile file,
ICollection fileTagNames,
string renamePattern)
diff --git a/src/AudioTagger.Library/UserSettings/Settings.cs b/src/AudioTagger.Library/UserSettings/Settings.cs
index 9945ccc..b926933 100644
--- a/src/AudioTagger.Library/UserSettings/Settings.cs
+++ b/src/AudioTagger.Library/UserSettings/Settings.cs
@@ -99,6 +99,17 @@ public sealed record Renaming
[JsonPropertyName("useAlbumDirectories")]
public bool UseAlbumDirectories { get; init; } = false;
+ ///
+ /// The Unicode normalization form to use for renamed filenames.
+ /// Valid values are `C`, `D`, `KC`, and `KD`.
+ /// `C` is used by default is no valid value is provided.
+ ///
+ /// Reference: https://unicode.org/reports/tr15/
+ /// Reference: https://en.wikipedia.org/wiki/Unicode_equivalence
+ ///
+ [JsonPropertyName("normalizationForm")]
+ public string NormalizationForm { get; init; } = "C";
+
[JsonPropertyName("ignoredDirectories")]
public ImmutableList? IgnoredDirectories { get; init; }
}