From 72ef75505b8929d0b8e60d43b0d13369584373d0 Mon Sep 17 00:00:00 2001 From: l4kr Date: Fri, 24 Jul 2026 14:30:34 +0200 Subject: [PATCH] Inject show name for bare-number episode filenames Previously, only S01E01-style episode codes triggered show-name injection from the folder name. Plain numbered filenames (e.g. "11 - Title.mkv") with a release tag sourced only from the folder (e.g. "[DmonHiro] Digimon Tamers (BD, 1080p)") were left untouched apart from the tag prefix. Also fixes ExtractShowName to strip trailing parenthetical metadata blocks like "(BD, 1080p)", which previously leaked into the derived show name. --- Futonizer.csproj | 4 ++-- Services/FileNameHelper.cs | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Futonizer.csproj b/Futonizer.csproj index da12e26..46adaac 100644 --- a/Futonizer.csproj +++ b/Futonizer.csproj @@ -9,8 +9,8 @@ enable Futonizer Futonizer - 1.0.13 - 1.0.13.0 + 1.0.14 + 1.0.14.0 Assets\app.ico diff --git a/Services/FileNameHelper.cs b/Services/FileNameHelper.cs index 79f600b..be9662e 100644 --- a/Services/FileNameHelper.cs +++ b/Services/FileNameHelper.cs @@ -60,6 +60,14 @@ public static class FileNameHelper private static readonly Regex EpisodeStartRegex = new(@"^(\d+|S\d+E\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + // Bare leading episode number with no tag of its own on the file (the + // release tag, if any, comes only from the folder), e.g. "11 - Title.mkv", + // "06_Title.mkv", "06.Title.mkv". Requires a separator right after the + // digits so resolution/quality tokens glued to more characters (e.g. + // "1080p") aren't mistaken for an episode number lead-in. + private static readonly Regex BareLeadingEpisodeNumberRegex = + new(@"^\d{1,3}(?=[\s._-])", RegexOptions.Compiled); + // Individual metadata-token patterns used to find where the show name ends // inside a folder name. private static readonly Regex[] MetadataPatterns = @@ -104,8 +112,9 @@ public static class FileNameHelper /// stripped and promoted to a bracket prefix. Takes priority over /// any folder-derived tag. /// Show-name injection — if the (now tag-free) filename starts with - /// an episode code (e.g. S01E01) the show name is derived from the - /// parent folder and prepended. + /// an episode code (e.g. S01E01), or with a bare episode number + /// (e.g. "11 - Title") once a release tag has been resolved, the + /// show name is derived from the parent folder and prepended. /// Release-tag prefix — a [TAG] bracket prefix, sourced from /// the filename suffix, the folder suffix, or the folder prefix, /// in that priority order. @@ -211,6 +220,17 @@ public static class FileNameHelper } } } + // Pattern C: file has no tag of its own — the tag came only from + // the folder — but the filename still starts with a bare + // episode number, e.g. "11 - Title.mkv" in a + // "[DmonHiro] Show Name (BD, 1080p)" folder. Inject the show + // name here; the tag is prepended separately in step 4. + else if (BareLeadingEpisodeNumberRegex.IsMatch(stemNoExt)) + { + string showName = ExtractShowName(folderName); + if (!string.IsNullOrEmpty(showName)) + fileName = showName + " " + fileName; + } } } @@ -273,6 +293,19 @@ public static class FileNameHelper name = FolderSuffixBracketTagRegex.Replace(name, "").Trim(); name = PrefixTagRegex.Replace(name, "").Trim(); + // Strip a trailing parenthesised metadata block, e.g. "(BD, 1080p)", + // when every comma/space-separated word inside it is recognisable + // quality/source metadata — so a real release group or descriptive + // parenthetical left at the end of the name isn't mistakenly dropped. + var trailingParenMatch = TrailingParenGroupRegex.Match(name); + if (trailingParenMatch.Success) + { + var parenWords = trailingParenMatch.Groups[1].Value + .Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); + if (parenWords.Length > 0 && parenWords.All(IsMetadataToken)) + name = name[..trailingParenMatch.Index].Trim(); + } + // Nothing left, or what remains immediately starts with a bracket — no show name. if (string.IsNullOrEmpty(name) || name[0] == '[') return string.Empty;