From 0cce61f6c7aecf358409660a84587ab583113b4b Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Sat, 18 Apr 2026 11:07:28 +0900 Subject: [PATCH 01/27] Use bind operator --- src/CCVTAC.Main/Settings/Settings.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CCVTAC.Main/Settings/Settings.fs b/src/CCVTAC.Main/Settings/Settings.fs index 40ae149..0ead3cc 100644 --- a/src/CCVTAC.Main/Settings/Settings.fs +++ b/src/CCVTAC.Main/Settings/Settings.fs @@ -3,6 +3,7 @@ namespace CCVTAC.Main.Settings open CCVTAC.Main open CCFSharpUtils open Spectre.Console +open FSharpPlus open System open System.Text.Json.Serialization @@ -185,7 +186,7 @@ module Settings = fileInfo.FullName |> File.ReadAllText |> deserialize - |> Result.bind validate + >>= validate with | :? FileNotFoundException -> Error $"File \"{fileInfo.FullName}\" was not found." | :? JsonException as e -> Error $"Parse error in \"{fileInfo.FullName}\": {e.Message}" From a12f158445114255eff3a0683e412c2021f644a1 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Sat, 18 Apr 2026 11:09:10 +0900 Subject: [PATCH 02/27] Refactor createSets func --- .../PostProcessing/Tagging/TaggingSet.fs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index 474e240..b3b7707 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -66,28 +66,28 @@ module TaggingSet = $"No image file found for video ID {videoId}." $"Multiple image files found for video ID {videoId}.") - /// Creates a collection of TaggingSets from a collection of file paths related to several video IDs. - /// Any extra, unnecessary files will be ignored. - /// Any validation errors will be accumulated and return in an Error. + /// Creates a collection of TaggingSets from a collection of file paths related to video IDs. + /// Irrelevant files are ignored. Validation errors are accumulated and returned in an Error. let createSets filePaths : Result = if Seq.isEmpty filePaths then Error ["No file paths to create a tagging set were provided."] else - let isRelevantFile fileName : Match option = + let relevantFileInfo fileName = // Regex group 0 is the full filename, and group 1 contains the video ID. let fileNamesHavingVideoIdsRgx = Regex(@".+\[([\w_\-]{11})\](?:.*)?\.(\w+)", RegexOptions.Compiled) - fileName |> Rgx.trySuccessMatch fileNamesHavingVideoIdsRgx - - let fileName (m: Match) = m.Groups[0].Value - let videoId (m: Match) = m.Groups[1].Value + fileName + |> Rgx.trySuccessMatch fileNamesHavingVideoIdsRgx + |> Option.map (fun m -> + {| FileName = m.Groups[0].Value + VideoId = m.Groups[1].Value |} ) filePaths |> List.ofSeq - |> List.choose isRelevantFile - |> List.groupBy videoId - |> List.mapSnd fileName + |> List.choose relevantFileInfo + |> List.groupBy _.VideoId + |> List.mapSnd _.FileName |> List.map createValidated |> List.sequenceResultA |!! List.collect id From 7a2d553f6bf468fd665a8f0b7cd32c7ad84d8988 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:50:53 +0900 Subject: [PATCH 03/27] Update package; use List.ensureOneV --- src/CCVTAC.Main/CCVTAC.Main.fsproj | 2 +- src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs | 10 ++-------- src/CCVTAC.Tests/CCVTAC.Tests.fsproj | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/CCVTAC.Main/CCVTAC.Main.fsproj b/src/CCVTAC.Main/CCVTAC.Main.fsproj index 96883f7..c812406 100644 --- a/src/CCVTAC.Main/CCVTAC.Main.fsproj +++ b/src/CCVTAC.Main/CCVTAC.Main.fsproj @@ -40,7 +40,7 @@ - + diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index b3b7707..7e8ddc5 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -37,12 +37,6 @@ module TaggingSet = then Ok xs else Error [errorMsg] - let ensureExactlyOne xs emptyErrorMsg multipleErrorMsg : Validation<'a, string> = - match xs with - | [] -> Error [emptyErrorMsg] - | [x] -> Ok x - | _ -> Error [multipleErrorMsg] - let hasSupportedAudioExt (fileName: string) = match Path.GetExtension fileName with | Null -> false @@ -59,10 +53,10 @@ module TaggingSet = (fun a j i -> create videoId a j i) (ensureNotEmpty audioFiles $"No supported audio files found for video ID {videoId}.") - (ensureExactlyOne jsonFiles + (List.ensureOneV jsonFiles $"No JSON file found for video ID {videoId}." $"Multiple JSON files found for video ID {videoId}.") - (ensureExactlyOne imageFiles + (List.ensureOneV imageFiles $"No image file found for video ID {videoId}." $"Multiple image files found for video ID {videoId}.") diff --git a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj index 47529a3..4051db9 100644 --- a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj +++ b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj @@ -14,7 +14,7 @@ - + From ad8fbf8421f4cb616db44431d2b6877e46e7a21b Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Sat, 18 Apr 2026 16:15:56 +0900 Subject: [PATCH 04/27] Update package; use List.ensureNotEmptyV --- src/CCVTAC.Main/CCVTAC.Main.fsproj | 2 +- src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs | 7 +------ src/CCVTAC.Tests/CCVTAC.Tests.fsproj | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/CCVTAC.Main/CCVTAC.Main.fsproj b/src/CCVTAC.Main/CCVTAC.Main.fsproj index c812406..5b5cff8 100644 --- a/src/CCVTAC.Main/CCVTAC.Main.fsproj +++ b/src/CCVTAC.Main/CCVTAC.Main.fsproj @@ -40,7 +40,7 @@ - + diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index 7e8ddc5..32c4d2e 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -32,11 +32,6 @@ module TaggingSet = ImageFile = i } let private createValidated (videoId, fileNames) : Result = - let ensureNotEmpty xs errorMsg : Validation<'a list, string> = - if List.isNotEmpty xs - then Ok xs - else Error [errorMsg] - let hasSupportedAudioExt (fileName: string) = match Path.GetExtension fileName with | Null -> false @@ -51,7 +46,7 @@ module TaggingSet = Validation.map3 (fun a j i -> create videoId a j i) - (ensureNotEmpty audioFiles + (List.ensureNotEmptyV audioFiles $"No supported audio files found for video ID {videoId}.") (List.ensureOneV jsonFiles $"No JSON file found for video ID {videoId}." diff --git a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj index 4051db9..deb5680 100644 --- a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj +++ b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj @@ -14,7 +14,7 @@ - + From a672c6dadd5655b3dedf0f3265ce1b85230d8500 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Sat, 18 Apr 2026 16:24:26 +0900 Subject: [PATCH 05/27] Add validation CE --- .../PostProcessing/Tagging/TaggingSet.fs | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index 32c4d2e..592c7a4 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -44,16 +44,28 @@ module TaggingSet = Files.imageFileExts |> List.collect (fun ext -> fileNames |> Files.filterByExt ext) - Validation.map3 - (fun a j i -> create videoId a j i) - (List.ensureNotEmptyV audioFiles - $"No supported audio files found for video ID {videoId}.") - (List.ensureOneV jsonFiles - $"No JSON file found for video ID {videoId}." - $"Multiple JSON files found for video ID {videoId}.") - (List.ensureOneV imageFiles - $"No image file found for video ID {videoId}." - $"Multiple image files found for video ID {videoId}.") + // Validation.map3 + // (fun a j i -> create videoId a j i) + // (List.ensureNotEmptyV audioFiles + // $"No supported audio files found for video ID {videoId}.") + // (List.ensureOneV jsonFiles + // $"No JSON file found for video ID {videoId}." + // $"Multiple JSON files found for video ID {videoId}.") + // (List.ensureOneV imageFiles + // $"No image file found for video ID {videoId}." + // $"Multiple image files found for video ID {videoId}.") + + validation { + let! a = List.ensureNotEmptyV audioFiles + $"No supported audio files found for video ID {videoId}." + let! j = List.ensureOneV jsonFiles + $"No JSON file found for video ID {videoId}." + $"Multiple JSON files found for video ID {videoId}." + let! i = List.ensureOneV imageFiles + $"No image file found for video ID {videoId}." + $"Multiple image files found for video ID {videoId}." + return create videoId a j i + } /// Creates a collection of TaggingSets from a collection of file paths related to video IDs. /// Irrelevant files are ignored. Validation errors are accumulated and returned in an Error. From 66dca6934fdc147076656d3658442c090e52cbf6 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Sat, 18 Apr 2026 16:55:58 +0900 Subject: [PATCH 06/27] Restore Validation.map3 due to 2 failing tests --- .../PostProcessing/Tagging/TaggingSet.fs | 42 +++++++++---------- .../PostProcessing/Tagging/TaggingSetTests.fs | 2 + 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index 592c7a4..7043f6a 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -44,28 +44,28 @@ module TaggingSet = Files.imageFileExts |> List.collect (fun ext -> fileNames |> Files.filterByExt ext) - // Validation.map3 - // (fun a j i -> create videoId a j i) - // (List.ensureNotEmptyV audioFiles - // $"No supported audio files found for video ID {videoId}.") - // (List.ensureOneV jsonFiles - // $"No JSON file found for video ID {videoId}." - // $"Multiple JSON files found for video ID {videoId}.") - // (List.ensureOneV imageFiles - // $"No image file found for video ID {videoId}." - // $"Multiple image files found for video ID {videoId}.") + Validation.map3 + (fun a j i -> create videoId a j i) + (List.ensureNotEmptyV audioFiles + $"No supported audio files found for video ID {videoId}.") + (List.ensureOneV jsonFiles + $"No JSON file found for video ID {videoId}." + $"Multiple JSON files found for video ID {videoId}.") + (List.ensureOneV imageFiles + $"No image file found for video ID {videoId}." + $"Multiple image files found for video ID {videoId}.") - validation { - let! a = List.ensureNotEmptyV audioFiles - $"No supported audio files found for video ID {videoId}." - let! j = List.ensureOneV jsonFiles - $"No JSON file found for video ID {videoId}." - $"Multiple JSON files found for video ID {videoId}." - let! i = List.ensureOneV imageFiles - $"No image file found for video ID {videoId}." - $"Multiple image files found for video ID {videoId}." - return create videoId a j i - } + // validation { + // let! a = List.ensureNotEmptyV audioFiles + // $"No supported audio files found for video ID {videoId}." + // let! j = List.ensureOneV jsonFiles + // $"No JSON file found for video ID {videoId}." + // $"Multiple JSON files found for video ID {videoId}." + // let! i = List.ensureOneV imageFiles + // $"No image file found for video ID {videoId}." + // $"Multiple image files found for video ID {videoId}." + // return create videoId a j i + // } /// Creates a collection of TaggingSets from a collection of file paths related to video IDs. /// Irrelevant files are ignored. Validation errors are accumulated and returned in an Error. diff --git a/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs b/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs index 679c0e1..66b9fae 100644 --- a/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs +++ b/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs @@ -90,6 +90,8 @@ module TaggingSetInstantiationTests = $"No image file found for video ID {videoId}."] let actual = createSets [audioFile] + printfn "① %A{expected}" |> ignore + printfn "② %A{actual}" |> ignore Assert.Equal(expected, actual) From b0bb31f91a5614f71685e1882cd07b0fbf1b7131 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Sun, 19 Apr 2026 10:34:58 +0900 Subject: [PATCH 07/27] Fix validation CE --- .../PostProcessing/Tagging/TaggingSet.fs | 42 +++++++++---------- .../PostProcessing/Tagging/TaggingSetTests.fs | 3 +- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index 7043f6a..86b56bf 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -44,28 +44,28 @@ module TaggingSet = Files.imageFileExts |> List.collect (fun ext -> fileNames |> Files.filterByExt ext) - Validation.map3 - (fun a j i -> create videoId a j i) - (List.ensureNotEmptyV audioFiles - $"No supported audio files found for video ID {videoId}.") - (List.ensureOneV jsonFiles - $"No JSON file found for video ID {videoId}." - $"Multiple JSON files found for video ID {videoId}.") - (List.ensureOneV imageFiles - $"No image file found for video ID {videoId}." - $"Multiple image files found for video ID {videoId}.") + // Validation.map3 + // (fun a j i -> create videoId a j i) + // (List.ensureNotEmptyV audioFiles + // $"No supported audio files found for video ID {videoId}.") + // (List.ensureOneV jsonFiles + // $"No JSON file found for video ID {videoId}." + // $"Multiple JSON files found for video ID {videoId}.") + // (List.ensureOneV imageFiles + // $"No image file found for video ID {videoId}." + // $"Multiple image files found for video ID {videoId}.") - // validation { - // let! a = List.ensureNotEmptyV audioFiles - // $"No supported audio files found for video ID {videoId}." - // let! j = List.ensureOneV jsonFiles - // $"No JSON file found for video ID {videoId}." - // $"Multiple JSON files found for video ID {videoId}." - // let! i = List.ensureOneV imageFiles - // $"No image file found for video ID {videoId}." - // $"Multiple image files found for video ID {videoId}." - // return create videoId a j i - // } + validation { + let! a = List.ensureNotEmptyV audioFiles + $"No supported audio files found for video ID {videoId}." + and! j = List.ensureOneV jsonFiles + $"No JSON file found for video ID {videoId}." + $"Multiple JSON files found for video ID {videoId}." + and! i = List.ensureOneV imageFiles + $"No image file found for video ID {videoId}." + $"Multiple image files found for video ID {videoId}." + return create videoId a j i + } /// Creates a collection of TaggingSets from a collection of file paths related to video IDs. /// Irrelevant files are ignored. Validation errors are accumulated and returned in an Error. diff --git a/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs b/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs index 66b9fae..bbefc5c 100644 --- a/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs +++ b/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs @@ -90,11 +90,10 @@ module TaggingSetInstantiationTests = $"No image file found for video ID {videoId}."] let actual = createSets [audioFile] - printfn "① %A{expected}" |> ignore - printfn "② %A{actual}" |> ignore Assert.Equal(expected, actual) + [] let ``parses multiple files from playlist`` () = let dir = Path.Combine("user", "Downloads", "Audio", "tmp") From f765080f81266309a0d1678bbff614ec5f3e256b Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:52:21 +0900 Subject: [PATCH 08/27] Use FileInfo in History --- src/CCVTAC.Main/History.fs | 8 ++++---- src/CCVTAC.Main/IoUtilities/Files.fs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/CCVTAC.Main/History.fs b/src/CCVTAC.Main/History.fs index 8f2b021..3c60b05 100644 --- a/src/CCVTAC.Main/History.fs +++ b/src/CCVTAC.Main/History.fs @@ -11,7 +11,7 @@ type History(filePath: string, displayCount: int) = let separator = ';' - member private _.FilePath = filePath + member private _.FileInfo = FileInfo filePath member private _.DisplayCount = displayCount /// Write a URL and its related data to the history file. @@ -20,16 +20,16 @@ type History(filePath: string, displayCount: int) = let serializedTime = JsonSerializer.Serialize(entryTime).Replace("\"", "") let text = serializedTime + string separator + url + String.newLine - match appendToFile this.FilePath text with + match appendToFile this.FileInfo text with | Ok _ -> printer.Debug $"Added \"%s{url}\" to the history log." - | Error err -> printer.Error $"Failed to write \"%s{url}\" to the history log at \"{this.FilePath}\": {err}" + | Error err -> printer.Error $"Failed to write \"%s{url}\" to the history log at \"{this.FileInfo}\": {err}" with exn -> printer.Error $"Could not append URL(s) to history log: {exn.Message}" member this.ShowRecent(printer: Printer) : unit = try let lines = - File.ReadAllLines this.FilePath + File.ReadAllLines this.FileInfo.FullName |> Seq.takeLast this.DisplayCount |> Seq.toList diff --git a/src/CCVTAC.Main/IoUtilities/Files.fs b/src/CCVTAC.Main/IoUtilities/Files.fs index 9dbefdd..617582c 100644 --- a/src/CCVTAC.Main/IoUtilities/Files.fs +++ b/src/CCVTAC.Main/IoUtilities/Files.fs @@ -18,5 +18,5 @@ module Files = let readAllText (filePath: string) : Result = ofTry (fun _ -> File.ReadAllText filePath) - let appendToFile (filePath: string) (text: string) : Result = - ofTry (fun _ -> File.AppendAllText(filePath, text)) + let appendToFile (file: FileInfo) (text: string) : Result = + ofTry (fun _ -> File.AppendAllText(file.FullName, text)) From 9a5bb5319af6cc8bced4a61a62963e4b6181adb6 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 22 Apr 2026 16:05:29 +0900 Subject: [PATCH 09/27] Update CCFSharpUtils package version --- src/CCVTAC.Main/CCVTAC.Main.fsproj | 2 +- src/CCVTAC.Tests/CCVTAC.Tests.fsproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CCVTAC.Main/CCVTAC.Main.fsproj b/src/CCVTAC.Main/CCVTAC.Main.fsproj index 5b5cff8..1c06eb9 100644 --- a/src/CCVTAC.Main/CCVTAC.Main.fsproj +++ b/src/CCVTAC.Main/CCVTAC.Main.fsproj @@ -40,7 +40,7 @@ - + diff --git a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj index deb5680..2cec62d 100644 --- a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj +++ b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj @@ -14,7 +14,7 @@ - + From 7be4513c17277a04bd29dde274fcf0a767ab14e9 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:03:18 +0900 Subject: [PATCH 10/27] Minor naming and layout --- src/CCVTAC.Main/InputHelper.fs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/CCVTAC.Main/InputHelper.fs b/src/CCVTAC.Main/InputHelper.fs index 84b7738..80c346b 100644 --- a/src/CCVTAC.Main/InputHelper.fs +++ b/src/CCVTAC.Main/InputHelper.fs @@ -52,13 +52,11 @@ module InputHelper = inputs |> List.map (fun input -> { Text = input - Category = if input[0] = Commands.prefix - then InputCategory.Command - else InputCategory.Url }) + Category = if input[0] = Commands.prefix then Command else Url }) let countCategories (inputs: CategorizedInput list) : CategoryCounts = inputs |> List.groupBy _.Category - |> List.map (fun (k, grp) -> k, grp |> Seq.length) + |> List.map (fun (category, items) -> category, Seq.length items) |> Map.ofSeq |> CategoryCounts From d411f9b6257ee313ef675a8528ebfc0f1834e632 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:09:15 +0900 Subject: [PATCH 11/27] Update CCFSharpUtils; update imports --- src/CCVTAC.Main/CCVTAC.Main.fsproj | 2 +- src/CCVTAC.Main/Commands.fs | 2 +- src/CCVTAC.Main/Downloading/Downloading.fs | 2 +- src/CCVTAC.Main/Downloading/Updater.fs | 2 +- src/CCVTAC.Main/ExternalTools/Runner.fs | 3 ++- src/CCVTAC.Main/History.fs | 3 ++- src/CCVTAC.Main/IoUtilities/Directories.fs | 3 ++- src/CCVTAC.Main/IoUtilities/Files.fs | 1 + src/CCVTAC.Main/Orchestrator.fs | 3 ++- src/CCVTAC.Main/PostProcessing/Deleter.fs | 2 +- src/CCVTAC.Main/PostProcessing/MetadataUtilities.fs | 3 +-- src/CCVTAC.Main/PostProcessing/Mover.fs | 2 ++ src/CCVTAC.Main/PostProcessing/PostProcessing.fs | 3 ++- src/CCVTAC.Main/PostProcessing/Renamer.fs | 5 +++-- src/CCVTAC.Main/PostProcessing/Tagging/Tagger.fs | 4 ++-- src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs | 3 ++- src/CCVTAC.Main/Printer.fs | 1 + src/CCVTAC.Main/Program.fs | 3 ++- src/CCVTAC.Main/ResultTracker.fs | 1 + src/CCVTAC.Main/Settings/Settings.fs | 2 +- src/CCVTAC.Tests/CCVTAC.Tests.fsproj | 4 ++-- .../{ => PostProcessing/Tagging}/TagDetectionTests.fs | 2 +- 22 files changed, 34 insertions(+), 22 deletions(-) rename src/CCVTAC.Tests/{ => PostProcessing/Tagging}/TagDetectionTests.fs (99%) diff --git a/src/CCVTAC.Main/CCVTAC.Main.fsproj b/src/CCVTAC.Main/CCVTAC.Main.fsproj index 1c06eb9..0ce451a 100644 --- a/src/CCVTAC.Main/CCVTAC.Main.fsproj +++ b/src/CCVTAC.Main/CCVTAC.Main.fsproj @@ -40,7 +40,7 @@ - + diff --git a/src/CCVTAC.Main/Commands.fs b/src/CCVTAC.Main/Commands.fs index a91d825..a96b485 100644 --- a/src/CCVTAC.Main/Commands.fs +++ b/src/CCVTAC.Main/Commands.fs @@ -1,6 +1,6 @@ namespace CCVTAC.Main -open CCFSharpUtils +open CCFSharpUtils.Text open System module Commands = diff --git a/src/CCVTAC.Main/Downloading/Downloading.fs b/src/CCVTAC.Main/Downloading/Downloading.fs index 5f8f89c..b7b0ad9 100644 --- a/src/CCVTAC.Main/Downloading/Downloading.fs +++ b/src/CCVTAC.Main/Downloading/Downloading.fs @@ -1,6 +1,6 @@ namespace CCVTAC.Main.Downloading -open CCFSharpUtils +open CCFSharpUtils.Text module Downloading = diff --git a/src/CCVTAC.Main/Downloading/Updater.fs b/src/CCVTAC.Main/Downloading/Updater.fs index 9aa8133..3d937e6 100644 --- a/src/CCVTAC.Main/Downloading/Updater.fs +++ b/src/CCVTAC.Main/Downloading/Updater.fs @@ -3,7 +3,7 @@ namespace CCVTAC.Main.Downloading open CCVTAC.Main open CCVTAC.Main.ExternalTools open CCVTAC.Main.Settings.Settings -open CCFSharpUtils +open CCFSharpUtils.Text module Updater = diff --git a/src/CCVTAC.Main/ExternalTools/Runner.fs b/src/CCVTAC.Main/ExternalTools/Runner.fs index 0659090..9ce28ec 100644 --- a/src/CCVTAC.Main/ExternalTools/Runner.fs +++ b/src/CCVTAC.Main/ExternalTools/Runner.fs @@ -1,7 +1,8 @@ namespace CCVTAC.Main.ExternalTools open CCVTAC.Main -open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open Startwatch.Library open System open System.Diagnostics diff --git a/src/CCVTAC.Main/History.fs b/src/CCVTAC.Main/History.fs index 3c60b05..c803126 100644 --- a/src/CCVTAC.Main/History.fs +++ b/src/CCVTAC.Main/History.fs @@ -1,7 +1,8 @@ namespace CCVTAC.Main open CCVTAC.Main.IoUtilities.Files -open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open System open System.IO open System.Text.Json diff --git a/src/CCVTAC.Main/IoUtilities/Directories.fs b/src/CCVTAC.Main/IoUtilities/Directories.fs index 1b75ccf..acd98fa 100644 --- a/src/CCVTAC.Main/IoUtilities/Directories.fs +++ b/src/CCVTAC.Main/IoUtilities/Directories.fs @@ -2,6 +2,8 @@ namespace CCVTAC.Main.IoUtilities open CCVTAC.Main open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open System.IO open System.Text @@ -96,4 +98,3 @@ module Directories = |> Ok with exn -> Error $"Error accessing or creating directory \"%s{dirName}\": %s{exn.Message}" - diff --git a/src/CCVTAC.Main/IoUtilities/Files.fs b/src/CCVTAC.Main/IoUtilities/Files.fs index 617582c..442dea5 100644 --- a/src/CCVTAC.Main/IoUtilities/Files.fs +++ b/src/CCVTAC.Main/IoUtilities/Files.fs @@ -1,6 +1,7 @@ namespace CCVTAC.Main.IoUtilities open CCFSharpUtils +open CCFSharpUtils.Text open System.IO module Files = diff --git a/src/CCVTAC.Main/Orchestrator.fs b/src/CCVTAC.Main/Orchestrator.fs index 4fff70b..6192d11 100644 --- a/src/CCVTAC.Main/Orchestrator.fs +++ b/src/CCVTAC.Main/Orchestrator.fs @@ -7,7 +7,8 @@ open CCVTAC.Main.PostProcessing open CCVTAC.Main.Settings open CCVTAC.Main.Settings.Settings open CCVTAC.Main.Settings.Settings.LiveUpdating -open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open Startwatch.Library open System diff --git a/src/CCVTAC.Main/PostProcessing/Deleter.fs b/src/CCVTAC.Main/PostProcessing/Deleter.fs index 36ca2a0..c0a64c8 100644 --- a/src/CCVTAC.Main/PostProcessing/Deleter.fs +++ b/src/CCVTAC.Main/PostProcessing/Deleter.fs @@ -1,7 +1,7 @@ namespace CCVTAC.Main.PostProcessing open CCVTAC.Main -open CCFSharpUtils +open CCFSharpUtils.Text open System.IO module Deleter = diff --git a/src/CCVTAC.Main/PostProcessing/MetadataUtilities.fs b/src/CCVTAC.Main/PostProcessing/MetadataUtilities.fs index 3916349..37174e8 100644 --- a/src/CCVTAC.Main/PostProcessing/MetadataUtilities.fs +++ b/src/CCVTAC.Main/PostProcessing/MetadataUtilities.fs @@ -1,7 +1,6 @@ namespace CCVTAC.Main.PostProcessing -open CCVTAC.Main -open CCFSharpUtils +open CCFSharpUtils.Text open System open System.Text diff --git a/src/CCVTAC.Main/PostProcessing/Mover.fs b/src/CCVTAC.Main/PostProcessing/Mover.fs index b783b7b..f3674c1 100644 --- a/src/CCVTAC.Main/PostProcessing/Mover.fs +++ b/src/CCVTAC.Main/PostProcessing/Mover.fs @@ -7,6 +7,8 @@ open CCVTAC.Main.PostProcessing open CCVTAC.Main.PostProcessing.Tagging open CCVTAC.Main.Settings.Settings open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open TaggingSet open System open System.IO diff --git a/src/CCVTAC.Main/PostProcessing/PostProcessing.fs b/src/CCVTAC.Main/PostProcessing/PostProcessing.fs index 62550a7..103e0e9 100644 --- a/src/CCVTAC.Main/PostProcessing/PostProcessing.fs +++ b/src/CCVTAC.Main/PostProcessing/PostProcessing.fs @@ -5,7 +5,8 @@ open CCVTAC.Main.IoUtilities open CCVTAC.Main.PostProcessing.Tagging open CCVTAC.Main.PostProcessing.Tagging.TaggingSet open CCVTAC.Main.Settings.Settings -open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open Startwatch.Library open System.IO open System.Linq diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index ecb1c4f..96c6eb7 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -1,9 +1,9 @@ namespace CCVTAC.Main.PostProcessing open CCVTAC.Main -open CCVTAC.Main.IoUtilities open CCVTAC.Main.Settings.Settings -open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open System open System.IO open System.Text @@ -11,6 +11,7 @@ open System.Text.RegularExpressions open Startwatch.Library module Renamer = + open CCVTAC.Main.IoUtilities let private toNormalizationForm (form: string) = match form.Trim().ToUpperInvariant() with diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/Tagger.fs b/src/CCVTAC.Main/PostProcessing/Tagging/Tagger.fs index 4f442ec..5a0bcfc 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/Tagger.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/Tagger.fs @@ -5,9 +5,9 @@ open CCVTAC.Main.Settings.Settings open CCVTAC.Main.PostProcessing open CCVTAC.Main.PostProcessing.Tagging open CCVTAC.Main.Downloading.Downloading -open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open Startwatch.Library -open TaggingSet open MetadataUtilities open System open System.IO diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index 86b56bf..79ac234 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -1,8 +1,9 @@ namespace CCVTAC.Main.PostProcessing.Tagging open CCVTAC.Main.IoUtilities -open CCFSharpUtils +open CCFSharpUtils.Collections open CCFSharpUtils.Operators +open CCFSharpUtils.Text open FsToolkit.ErrorHandling open System.IO open System.Text.RegularExpressions diff --git a/src/CCVTAC.Main/Printer.fs b/src/CCVTAC.Main/Printer.fs index e4a9e88..bbe820d 100644 --- a/src/CCVTAC.Main/Printer.fs +++ b/src/CCVTAC.Main/Printer.fs @@ -1,6 +1,7 @@ namespace CCVTAC.Main open CCFSharpUtils +open CCFSharpUtils.Text open System open System.Collections.Generic open System.Linq diff --git a/src/CCVTAC.Main/Program.fs b/src/CCVTAC.Main/Program.fs index 4a68add..6f78df7 100644 --- a/src/CCVTAC.Main/Program.fs +++ b/src/CCVTAC.Main/Program.fs @@ -5,7 +5,8 @@ open CCVTAC.Main.IoUtilities open CCVTAC.Main.Settings open CCVTAC.Main.Settings.Settings open Settings.IO -open CCFSharpUtils +open CCFSharpUtils.Collections +open CCFSharpUtils.Text open Spectre.Console open System open System.IO diff --git a/src/CCVTAC.Main/ResultTracker.fs b/src/CCVTAC.Main/ResultTracker.fs index 1aab17f..2787c2a 100644 --- a/src/CCVTAC.Main/ResultTracker.fs +++ b/src/CCVTAC.Main/ResultTracker.fs @@ -1,6 +1,7 @@ namespace CCVTAC.Main open CCFSharpUtils +open CCFSharpUtils.Text open System open System.Collections.Generic diff --git a/src/CCVTAC.Main/Settings/Settings.fs b/src/CCVTAC.Main/Settings/Settings.fs index 0ead3cc..1388721 100644 --- a/src/CCVTAC.Main/Settings/Settings.fs +++ b/src/CCVTAC.Main/Settings/Settings.fs @@ -1,7 +1,7 @@ namespace CCVTAC.Main.Settings open CCVTAC.Main -open CCFSharpUtils +open CCFSharpUtils.Text open Spectre.Console open FSharpPlus open System diff --git a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj index 2cec62d..7be8ea2 100644 --- a/src/CCVTAC.Tests/CCVTAC.Tests.fsproj +++ b/src/CCVTAC.Tests/CCVTAC.Tests.fsproj @@ -8,13 +8,13 @@ - + - + diff --git a/src/CCVTAC.Tests/TagDetectionTests.fs b/src/CCVTAC.Tests/PostProcessing/Tagging/TagDetectionTests.fs similarity index 99% rename from src/CCVTAC.Tests/TagDetectionTests.fs rename to src/CCVTAC.Tests/PostProcessing/Tagging/TagDetectionTests.fs index 10ece0b..6159704 100644 --- a/src/CCVTAC.Tests/TagDetectionTests.fs +++ b/src/CCVTAC.Tests/PostProcessing/Tagging/TagDetectionTests.fs @@ -3,7 +3,7 @@ module TagDetectionTests open CCVTAC.Main.PostProcessing.Tagging open CCVTAC.Main.PostProcessing open CCVTAC.Main.Settings.Settings -open CCFSharpUtils +open CCFSharpUtils.Text open System open Xunit From 0f4b322a45ea6ed8261114665f4c55b361da7de3 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 26 May 2026 21:26:49 +0900 Subject: [PATCH 12/27] Erase Validation.map3 --- src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs index 79ac234..777d14f 100644 --- a/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs +++ b/src/CCVTAC.Main/PostProcessing/Tagging/TaggingSet.fs @@ -45,17 +45,6 @@ module TaggingSet = Files.imageFileExts |> List.collect (fun ext -> fileNames |> Files.filterByExt ext) - // Validation.map3 - // (fun a j i -> create videoId a j i) - // (List.ensureNotEmptyV audioFiles - // $"No supported audio files found for video ID {videoId}.") - // (List.ensureOneV jsonFiles - // $"No JSON file found for video ID {videoId}." - // $"Multiple JSON files found for video ID {videoId}.") - // (List.ensureOneV imageFiles - // $"No image file found for video ID {videoId}." - // $"Multiple image files found for video ID {videoId}.") - validation { let! a = List.ensureNotEmptyV audioFiles $"No supported audio files found for video ID {videoId}." From 63c90f7f1de16abf7a4a7bfa7ee8d837350d7f2d Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 26 May 2026 21:27:56 +0900 Subject: [PATCH 13/27] Move 'open' --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index 96c6eb7..caa7a9f 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -1,6 +1,7 @@ namespace CCVTAC.Main.PostProcessing open CCVTAC.Main +open CCVTAC.Main.IoUtilities open CCVTAC.Main.Settings.Settings open CCFSharpUtils.Collections open CCFSharpUtils.Text @@ -11,7 +12,6 @@ open System.Text.RegularExpressions open Startwatch.Library module Renamer = - open CCVTAC.Main.IoUtilities let private toNormalizationForm (form: string) = match form.Trim().ToUpperInvariant() with From dbb4a2ec9853f354fe420c3730731d5719aea123 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 26 May 2026 21:29:18 +0900 Subject: [PATCH 14/27] Remove empty line --- src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs b/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs index bbefc5c..679c0e1 100644 --- a/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs +++ b/src/CCVTAC.Tests/PostProcessing/Tagging/TaggingSetTests.fs @@ -93,7 +93,6 @@ module TaggingSetInstantiationTests = Assert.Equal(expected, actual) - [] let ``parses multiple files from playlist`` () = let dir = Path.Combine("user", "Downloads", "Audio", "tmp") From ceb4da88e86eaf6d6462fd675068fc9b87738294 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 26 May 2026 21:31:17 +0900 Subject: [PATCH 15/27] Add 'no rename pattern matches' message --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index caa7a9f..e57809a 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -30,7 +30,9 @@ module Renamer = |> Seq.toList if List.isEmpty matches - then sb + then + printer.Debug "No rename pattern matches found." + sb else if not isQuietMode then let patternSummary = From 958ac0e5753f002508f40f48324cbf2726fa6ba2 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 27 May 2026 11:40:43 +0900 Subject: [PATCH 16/27] Add return type --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index e57809a..78ae121 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -20,7 +20,7 @@ module Renamer = | "KC" -> NormalizationForm.FormKC | _ -> NormalizationForm.FormC - let updateTextViaPatterns isQuietMode (printer: Printer) (sb: StringBuilder) (renamePattern: RenamePattern) = + let updateTextViaPatterns isQuietMode (printer: Printer) (sb: StringBuilder) (renamePattern: RenamePattern) : StringBuilder = let regex = Regex renamePattern.RegexPattern let matches = @@ -36,10 +36,9 @@ module Renamer = else if not isQuietMode then let patternSummary = - if String.hasText renamePattern.Summary then - $"\"%s{renamePattern.Summary}\"" - else - $"`%s{renamePattern.RegexPattern}` (no description)" + if String.hasText renamePattern.Summary + then $"\"%s{renamePattern.Summary}\"" + else $"`%s{renamePattern.RegexPattern}` (no description)" printer.Debug $"Rename pattern %s{patternSummary} matched × %d{matches.Length}." From bab97a0e6d8b306de9cd08961153c88e741e2717 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 27 May 2026 20:42:20 +0900 Subject: [PATCH 17/27] Updates to updateTextViaPatterns --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index 78ae121..15ea403 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -3,6 +3,7 @@ namespace CCVTAC.Main.PostProcessing open CCVTAC.Main open CCVTAC.Main.IoUtilities open CCVTAC.Main.Settings.Settings +open CCFSharpUtils open CCFSharpUtils.Collections open CCFSharpUtils.Text open System @@ -20,7 +21,7 @@ module Renamer = | "KC" -> NormalizationForm.FormKC | _ -> NormalizationForm.FormC - let updateTextViaPatterns isQuietMode (printer: Printer) (sb: StringBuilder) (renamePattern: RenamePattern) : StringBuilder = + let updateTextViaPatterns isQuietMode (printer: Printer) (sb: SB) (renamePattern: RenamePattern) : SB = let regex = Regex renamePattern.RegexPattern let matches = @@ -50,16 +51,17 @@ module Renamer = m |> Rgx.groups |> Seq.mapi (fun i _ -> - let searchFor = sprintf "%%<%d>s" (i + 1) + let groupIndex = i + 1 + let searchFor = sprintf "%%<%d>s" groupIndex let replaceWith = // Group 0 is the entire match, so we only want groups starting at 1. if i + 1 < m.Groups.Count - then m.Groups[i + 1].Value.Trim() + then m.Groups[groupIndex].Value.Trim() else String.Empty (searchFor, replaceWith)) |> Seq.fold - (fun (sb': StringBuilder) -> sb'.Replace) - (StringBuilder renamePattern.ReplaceWithPattern) + (fun (sb': SB) (searchFor, replaceWith) -> sb'.Replace(searchFor, replaceWith)) + (SB renamePattern.ReplaceWithPattern) |> _.ToString() sb.Insert(m.Index, replacementText) |> ignore @@ -84,8 +86,8 @@ module Renamer = let newFileName = userSettings.RenamePatterns |> List.fold - (fun (sb: StringBuilder) -> updateTextViaPatterns userSettings.QuietMode printer sb) - (StringBuilder audioFile.Name) + (fun (sb: SB) -> updateTextViaPatterns userSettings.QuietMode printer sb) + (SB audioFile.Name) |> _.ToString() try From 9c518ff4ab6d57d97220240ac208357edf8d07c7 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 27 May 2026 21:55:19 +0900 Subject: [PATCH 18/27] Refactor updateTextViaPatterns --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 44 +++++++++-------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index 15ea403..b0dc948 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -30,41 +30,31 @@ module Renamer = |> Seq.rev |> Seq.toList - if List.isEmpty matches - then + let buildReplacementText (renamePattern: RenamePattern) (m: Match) : string = + let sb = SB renamePattern.ReplaceWithPattern + + for i = 1 to m.Groups.Count - 1 do + let placeholder = sprintf "%%<%d>s" i + let value = if i < m.Groups.Count then m.Groups[i].Value.Trim() else String.Empty + sb.Replace(placeholder, value) |> ignore + + sb.ToString() + + match matches with + | [] -> printer.Debug "No rename pattern matches found." sb - else + | _ -> if not isQuietMode then - let patternSummary = + let patternDesc = if String.hasText renamePattern.Summary then $"\"%s{renamePattern.Summary}\"" else $"`%s{renamePattern.RegexPattern}` (no description)" - - printer.Debug $"Rename pattern %s{patternSummary} matched × %d{matches.Length}." + printer.Debug $"Rename pattern %s{patternDesc} matched × %d{matches.Length}." for m in matches do - sb.Remove(m.Index, m.Length) |> ignore - - // Build replacement text by replacing % placeholders with group captures. - let replacementText = - m - |> Rgx.groups - |> Seq.mapi (fun i _ -> - let groupIndex = i + 1 - let searchFor = sprintf "%%<%d>s" groupIndex - let replaceWith = - // Group 0 is the entire match, so we only want groups starting at 1. - if i + 1 < m.Groups.Count - then m.Groups[groupIndex].Value.Trim() - else String.Empty - (searchFor, replaceWith)) - |> Seq.fold - (fun (sb': SB) (searchFor, replaceWith) -> sb'.Replace(searchFor, replaceWith)) - (SB renamePattern.ReplaceWithPattern) - |> _.ToString() - - sb.Insert(m.Index, replacementText) |> ignore + let replacementText = buildReplacementText renamePattern m + sb.Remove(m.Index, m.Length).Insert(m.Index, replacementText) |> ignore sb From 9a0fce348c91e9f570c9c8608d4d0a35de989806 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 27 May 2026 21:58:25 +0900 Subject: [PATCH 19/27] Replace List.isEmpty with pattern matching --- src/CCVTAC.Main/Orchestrator.fs | 5 +++-- src/CCVTAC.Main/PostProcessing/Renamer.fs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/CCVTAC.Main/Orchestrator.fs b/src/CCVTAC.Main/Orchestrator.fs index 6192d11..96749ba 100644 --- a/src/CCVTAC.Main/Orchestrator.fs +++ b/src/CCVTAC.Main/Orchestrator.fs @@ -293,9 +293,10 @@ module Orchestrator = let input = printer.GetInput prompt let splitInputs = splitInputText input - if List.isEmpty splitInputs then + match splitInputs with + | [] -> printer.Error $"Invalid input. Enter only URLs or commands beginning with \"%c{Commands.prefix}\"." - else + | _ -> let categorizedInputs = categorizeInputs splitInputs let categoryCounts = countCategories categorizedInputs summarizeInput categorizedInputs categoryCounts printer diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index b0dc948..aa0fbb0 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -67,9 +67,10 @@ module Renamer = |> Seq.filter (fun f -> List.containsIgnoreCase f.Extension Files.audioFileExts) |> List.ofSeq - if List.isEmpty audioFiles then + match audioFiles with + | [] -> printer.Warning "No audio files to rename were found." - else + | _ -> printer.Debug $"""Renaming %s{String.fileLabelWithDesc "audio" audioFiles.Length}...""" for audioFile in audioFiles do From 86cb38cc8f614eec0e5d4595e7147330e7f02856 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Thu, 28 May 2026 09:46:35 +0900 Subject: [PATCH 20/27] Remove unneeded no-match messages during renaming --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index aa0fbb0..c57b861 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -42,7 +42,6 @@ module Renamer = match matches with | [] -> - printer.Debug "No rename pattern matches found." sb | _ -> if not isQuietMode then @@ -50,7 +49,7 @@ module Renamer = if String.hasText renamePattern.Summary then $"\"%s{renamePattern.Summary}\"" else $"`%s{renamePattern.RegexPattern}` (no description)" - printer.Debug $"Rename pattern %s{patternDesc} matched × %d{matches.Length}." + printer.Debug $"> Rename pattern %s{patternDesc} matched (%d{matches.Length}×)." for m in matches do let replacementText = buildReplacementText renamePattern m From 985048e479d485500e3946111b371bada1cc1820 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Thu, 28 May 2026 11:53:44 +0900 Subject: [PATCH 21/27] Add func comment --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index c57b861..5d711e7 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -30,6 +30,8 @@ module Renamer = |> Seq.rev |> Seq.toList + /// Builds replacement text by substituting %s placeholders with captured regex group values. + /// Group values are trimmed, and indexing starts from 1 (because group 0 is the full match). let buildReplacementText (renamePattern: RenamePattern) (m: Match) : string = let sb = SB renamePattern.ReplaceWithPattern From e88cecc86ce578ff5ef93a678cac02e8bff53b5e Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Thu, 28 May 2026 17:47:51 +0900 Subject: [PATCH 22/27] Revert pattern matching --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index 5d711e7..a7ffd35 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -42,10 +42,9 @@ module Renamer = sb.ToString() - match matches with - | [] -> + if List.isEmpty matches then sb - | _ -> + else if not isQuietMode then let patternDesc = if String.hasText renamePattern.Summary From c9e6aa5ad5001de7c741490f99d767df25a8aa1f Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Thu, 28 May 2026 17:51:53 +0900 Subject: [PATCH 23/27] Combine code to one line --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index a7ffd35..b45deb7 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -21,14 +21,10 @@ module Renamer = | "KC" -> NormalizationForm.FormKC | _ -> NormalizationForm.FormC - let updateTextViaPatterns isQuietMode (printer: Printer) (sb: SB) (renamePattern: RenamePattern) : SB = + let updateTextViaPatterns isQuietMode (printer: Printer) (text: SB) (renamePattern: RenamePattern) : SB = let regex = Regex renamePattern.RegexPattern - let matches = - regex.Matches(sb.ToString()) - |> Rgx.successMatches - |> Seq.rev - |> Seq.toList + let matches = text.ToString() |> regex.Matches |> Rgx.successMatches |> Seq.rev |> Seq.toList /// Builds replacement text by substituting %s placeholders with captured regex group values. /// Group values are trimmed, and indexing starts from 1 (because group 0 is the full match). @@ -43,7 +39,7 @@ module Renamer = sb.ToString() if List.isEmpty matches then - sb + text else if not isQuietMode then let patternDesc = @@ -54,9 +50,9 @@ module Renamer = for m in matches do let replacementText = buildReplacementText renamePattern m - sb.Remove(m.Index, m.Length).Insert(m.Index, replacementText) |> ignore + text.Remove(m.Index, m.Length).Insert(m.Index, replacementText) |> ignore - sb + text let run userSettings workingDirectory (printer: Printer) : unit = let watch = Watch() From f6076d9ad69f9e89ee34a137b772a08f811b725f Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Thu, 28 May 2026 18:01:18 +0900 Subject: [PATCH 24/27] Move summary printing to func --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 17 +++++++++-------- src/CCVTAC.Tests/RenamerTests.fs | 4 +--- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index b45deb7..08b22fd 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -21,11 +21,17 @@ module Renamer = | "KC" -> NormalizationForm.FormKC | _ -> NormalizationForm.FormC - let updateTextViaPatterns isQuietMode (printer: Printer) (text: SB) (renamePattern: RenamePattern) : SB = + let updateTextViaPattern isQuietMode (printer: Printer) (text: SB) (renamePattern: RenamePattern) : SB = let regex = Regex renamePattern.RegexPattern let matches = text.ToString() |> regex.Matches |> Rgx.successMatches |> Seq.rev |> Seq.toList + let printSummary: unit = + let patternDesc = if String.hasText renamePattern.Summary + then $"\"%s{renamePattern.Summary}\"" + else $"`%s{renamePattern.RegexPattern}` (no description)" + printer.Debug $"> Rename pattern %s{patternDesc} matched (%d{matches.Length}×)." + /// Builds replacement text by substituting %s placeholders with captured regex group values. /// Group values are trimmed, and indexing starts from 1 (because group 0 is the full match). let buildReplacementText (renamePattern: RenamePattern) (m: Match) : string = @@ -41,12 +47,7 @@ module Renamer = if List.isEmpty matches then text else - if not isQuietMode then - let patternDesc = - if String.hasText renamePattern.Summary - then $"\"%s{renamePattern.Summary}\"" - else $"`%s{renamePattern.RegexPattern}` (no description)" - printer.Debug $"> Rename pattern %s{patternDesc} matched (%d{matches.Length}×)." + if not isQuietMode then printSummary for m in matches do let replacementText = buildReplacementText renamePattern m @@ -73,7 +74,7 @@ module Renamer = let newFileName = userSettings.RenamePatterns |> List.fold - (fun (sb: SB) -> updateTextViaPatterns userSettings.QuietMode printer sb) + (fun (sb: SB) -> updateTextViaPattern userSettings.QuietMode printer sb) (SB audioFile.Name) |> _.ToString() diff --git a/src/CCVTAC.Tests/RenamerTests.fs b/src/CCVTAC.Tests/RenamerTests.fs index 8453597..5598e05 100644 --- a/src/CCVTAC.Tests/RenamerTests.fs +++ b/src/CCVTAC.Tests/RenamerTests.fs @@ -36,11 +36,9 @@ module UpdateTextViaPatternsTests = let actual = List.fold - (fun sb pattern -> Renamer.updateTextViaPatterns true (Printer false) sb pattern) + (fun sb pattern -> Renamer.updateTextViaPattern true (Printer false) sb pattern) fileName patterns |> _.ToString() Assert.Equal(expected, actual) - - From 78fdfe9ee18fa7d37ba5f849526c643ed543e5e2 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Fri, 29 May 2026 12:50:27 +0900 Subject: [PATCH 25/27] Convert value to func --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index 08b22fd..ca9d06b 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -26,7 +26,7 @@ module Renamer = let matches = text.ToString() |> regex.Matches |> Rgx.successMatches |> Seq.rev |> Seq.toList - let printSummary: unit = + let printSummary () = let patternDesc = if String.hasText renamePattern.Summary then $"\"%s{renamePattern.Summary}\"" else $"`%s{renamePattern.RegexPattern}` (no description)" @@ -47,7 +47,7 @@ module Renamer = if List.isEmpty matches then text else - if not isQuietMode then printSummary + if not isQuietMode then printSummary () for m in matches do let replacementText = buildReplacementText renamePattern m From 0ead7734be44321101fe1ebbfbce956beea2edc5 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Fri, 29 May 2026 13:00:07 +0900 Subject: [PATCH 26/27] Rename var --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index ca9d06b..9d295f9 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -74,7 +74,7 @@ module Renamer = let newFileName = userSettings.RenamePatterns |> List.fold - (fun (sb: SB) -> updateTextViaPattern userSettings.QuietMode printer sb) + (fun acc -> updateTextViaPattern userSettings.QuietMode printer acc) (SB audioFile.Name) |> _.ToString() From 699d8382effb6cdcc8b5d4500ab21d6064eec6d0 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Fri, 29 May 2026 14:52:42 +0900 Subject: [PATCH 27/27] Revert match call --- src/CCVTAC.Main/PostProcessing/Renamer.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/CCVTAC.Main/PostProcessing/Renamer.fs b/src/CCVTAC.Main/PostProcessing/Renamer.fs index 9d295f9..2a7600e 100644 --- a/src/CCVTAC.Main/PostProcessing/Renamer.fs +++ b/src/CCVTAC.Main/PostProcessing/Renamer.fs @@ -64,10 +64,9 @@ module Renamer = |> Seq.filter (fun f -> List.containsIgnoreCase f.Extension Files.audioFileExts) |> List.ofSeq - match audioFiles with - | [] -> + if List.isEmpty audioFiles then printer.Warning "No audio files to rename were found." - | _ -> + else printer.Debug $"""Renaming %s{String.fileLabelWithDesc "audio" audioFiles.Length}...""" for audioFile in audioFiles do