diff --git a/Futonizer.csproj b/Futonizer.csproj index 76c76ed..3263779 100644 --- a/Futonizer.csproj +++ b/Futonizer.csproj @@ -9,8 +9,8 @@ enable Futonizer Futonizer - 1.0.7 - 1.0.7.0 + 1.0.8 + 1.0.8.0 Assets\app.ico diff --git a/Services/FileNameHelper.cs b/Services/FileNameHelper.cs index 678afc4..c963cb4 100644 --- a/Services/FileNameHelper.cs +++ b/Services/FileNameHelper.cs @@ -20,6 +20,13 @@ public static class FileNameHelper private static readonly Regex FileSuffixTagRegex = new(@"-([A-Za-z][A-Za-z0-9]{2,20})$", RegexOptions.Compiled); + // Trailing parenthesised metadata block ending in the release group as its + // last word, e.g. "... (1080p AMZN WEB-DL x265 Celdra)" → group="Celdra". + // Common on Amazon/streaming rips that don't set the group off with a + // "-" or its own brackets. + private static readonly Regex TrailingParenGroupRegex = + new(@"\(([^()]*)\)$", RegexOptions.Compiled); + // Prefix release tag: "[KAA] Show Name" → tag = "KAA" private static readonly Regex PrefixTagRegex = new(@"^\[(.+?)\]", RegexOptions.Compiled); @@ -45,7 +52,8 @@ public static class FileNameHelper new(@"^(BD|BluRay|Blu-Ray|WEB|WEB-DL|WEBRip|HDTV|AMZN|NF|DSNP|HULU|MAX)$",RegexOptions.IgnoreCase | RegexOptions.Compiled), // sources new(@"^(Remux|Encode)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // processing new(@"^(HEVC|AVC|x264|x265|H\.?264|H\.?265|VP9)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // video codecs - new(@"^(FLAC|AAC|DTS|TrueHD|Atmos|AC3|EAC3|Opus|MP3)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // audio codecs + new(@"^(FLAC|AAC|DTS(-HD)?|TrueHD|Atmos|E?AC3|DDP?|Opus|MP3)(\d(\.\d)?)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // audio codecs (+ optional channel suffix, e.g. DDP5.1) + new(@"^\d\.\d$", RegexOptions.Compiled), // bare channel count, e.g. 5.1 new(@"^(HDR|SDR|10bit|8bit|HDR10|DV|DoVi)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // HDR / bit-depth ]; @@ -56,8 +64,10 @@ public static class FileNameHelper /// (in order): /// /// Filename suffix tag extraction — a trailing -GroupName on - /// the filename stem is stripped and promoted to a bracket prefix. - /// Takes priority over any folder-derived tag. + /// the filename stem, or a release group tucked in as the last word + /// of a trailing (...) metadata block (e.g. Amazon rips like + /// (1080p AMZN WEB-DL x265 Celdra)), is 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. @@ -85,6 +95,31 @@ public static class FileNameHelper stemNoExt = stemNoExt[..fileSuffixMatch.Index]; fileName = stemNoExt + ext; } + else + { + // 1b. Extract a release group tucked in as the last word of a + // trailing "(...)" metadata block, e.g. + // "... (1080p AMZN WEB-DL x265 Celdra)" → tag="Celdra", + // stem="... (1080p AMZN WEB-DL x265)". + var parenMatch = TrailingParenGroupRegex.Match(stemNoExt); + if (parenMatch.Success) + { + string parenContent = parenMatch.Groups[1].Value; + var words = parenContent.Split(' ', StringSplitOptions.RemoveEmptyEntries); + // Only treat the last word as a release group if the rest of the + // block is recognisable quality/source/codec metadata — this + // avoids misfiring on unrelated parentheticals like "(2020)" or + // "(Dual Audio)" that don't carry a group name. + if (words.Length >= 2 && !IsMetadataToken(words[^1]) && words[..^1].Any(IsMetadataToken)) + { + fileTag = words[^1]; + string remaining = string.Join(" ", words[..^1]); + string beforeParen = stemNoExt[..parenMatch.Index].TrimEnd(); + stemNoExt = string.IsNullOrEmpty(remaining) ? beforeParen : $"{beforeParen} ({remaining})"; + fileName = stemNoExt + ext; + } + } + } // 2. Determine the release tag: filename suffix > folder tag. string? tag = fileTag;