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;