- Suffix release tag support: folder name ending in -TAG prepends [TAG] to output filenames - Show name injection from parent folder for episode-only filenames (e.g. S01E01-Name.mkv) - Queue and logs display predicted output filename - Run button label reflects rename when applicable - Gray out main UI while strip/rename is in progress
133 lines
6.1 KiB
C#
133 lines
6.1 KiB
C#
using System.IO;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Futonizer.Services;
|
|
|
|
/// <summary>
|
|
/// Pure static helpers for computing the final output filename from a source
|
|
/// file path, applying release-tag prefixing and show-name injection.
|
|
/// </summary>
|
|
public static class FileNameHelper
|
|
{
|
|
// Suffix release tag: "Show Name-TTGA" → tag = "TTGA"
|
|
// Must be 2-8 uppercase letters/digits at the very end, preceded by a dash.
|
|
private static readonly Regex SuffixTagRegex =
|
|
new(@"-([A-Z][A-Z0-9]{1,7})$", RegexOptions.Compiled);
|
|
|
|
// Prefix release tag: "[KAA] Show Name" → tag = "KAA"
|
|
private static readonly Regex PrefixTagRegex =
|
|
new(@"^\[(.+?)\]", RegexOptions.Compiled);
|
|
|
|
// Episode-only filename: starts with S01E01, S1E1, etc. (no show name present)
|
|
private static readonly Regex EpisodeOnlyRegex =
|
|
new(@"^S\d+E\d+", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
|
|
// Individual metadata-token patterns used to find where the show name ends
|
|
// inside a folder name.
|
|
private static readonly Regex[] MetadataPatterns =
|
|
[
|
|
new(@"^S\d+$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // S01
|
|
new(@"^S\d+E\d+", RegexOptions.IgnoreCase | RegexOptions.Compiled), // S01E01...
|
|
new(@"^E[Pp]?\d+$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // E01, EP01
|
|
new(@"^\d{3,4}[pP]$", RegexOptions.Compiled), // 1080p, 720p
|
|
new(@"^(4K|UHD|2160p)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // 4K, UHD
|
|
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(@"^(HDR|SDR|10bit|8bit|HDR10|DV|DoVi)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), // HDR / bit-depth
|
|
];
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Computes the final output filename for a given source file path, applying
|
|
/// (in order):
|
|
/// <list type="number">
|
|
/// <item>Show-name injection — if the filename starts with an episode code
|
|
/// (e.g. S01E01) the show name is derived from the parent folder and
|
|
/// prepended.</item>
|
|
/// <item>Release-tag prefix — a <c>[TAG]</c> bracket prefix derived from
|
|
/// the parent folder name, either from a suffix pattern (<c>-TAG</c>)
|
|
/// or a prefix pattern (<c>[TAG]</c>).</item>
|
|
/// </list>
|
|
/// Returns the original filename unchanged when the parent folder provides
|
|
/// no actionable information.
|
|
/// </summary>
|
|
public static string ComputeOutputFileName(string sourceFilePath)
|
|
{
|
|
string fileName = Path.GetFileName(sourceFilePath);
|
|
string? folder = Path.GetDirectoryName(sourceFilePath);
|
|
if (string.IsNullOrEmpty(folder)) return fileName;
|
|
|
|
string folderName = Path.GetFileName(folder);
|
|
if (string.IsNullOrEmpty(folderName)) return fileName;
|
|
|
|
// 1. Inject show name when only an episode code is present.
|
|
string fileNameNoExt = Path.GetFileNameWithoutExtension(fileName);
|
|
if (EpisodeOnlyRegex.IsMatch(fileNameNoExt))
|
|
{
|
|
string showName = ExtractShowName(folderName);
|
|
if (!string.IsNullOrEmpty(showName))
|
|
fileName = showName + " " + fileName;
|
|
}
|
|
|
|
// 2. Prepend release tag if not already present.
|
|
string? tag = ExtractReleaseTag(folderName);
|
|
if (!string.IsNullOrEmpty(tag))
|
|
{
|
|
string tagPrefix = $"[{tag}]";
|
|
if (!fileName.StartsWith(tagPrefix, StringComparison.OrdinalIgnoreCase))
|
|
fileName = tagPrefix + " " + fileName;
|
|
}
|
|
|
|
return fileName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts the release tag string (without brackets) from a folder name.
|
|
/// Suffix pattern (<c>-TAG</c>) takes priority over prefix pattern
|
|
/// (<c>[TAG]</c>). Returns <c>null</c> when neither is present.
|
|
/// </summary>
|
|
public static string? ExtractReleaseTag(string folderName)
|
|
{
|
|
var suffixMatch = SuffixTagRegex.Match(folderName);
|
|
if (suffixMatch.Success) return suffixMatch.Groups[1].Value;
|
|
|
|
var prefixMatch = PrefixTagRegex.Match(folderName);
|
|
if (prefixMatch.Success) return prefixMatch.Groups[1].Value;
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Derives the show name from a folder name by stripping any release tag
|
|
/// (prefix or suffix) and then taking all words that precede the first
|
|
/// recognised metadata token (season, quality, source, codec, etc.).
|
|
/// </summary>
|
|
public static string ExtractShowName(string folderName)
|
|
{
|
|
// Strip release tags first.
|
|
string name = SuffixTagRegex.Replace(folderName, "").Trim();
|
|
name = PrefixTagRegex.Replace(name, "").Trim();
|
|
|
|
// Collect words up to the first metadata token.
|
|
var words = name.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
|
var showWords = new List<string>();
|
|
foreach (var word in words)
|
|
{
|
|
if (IsMetadataToken(word)) break;
|
|
showWords.Add(word);
|
|
}
|
|
|
|
return string.Join(" ", showWords).Trim();
|
|
}
|
|
|
|
private static bool IsMetadataToken(string token)
|
|
{
|
|
foreach (var pattern in MetadataPatterns)
|
|
if (pattern.IsMatch(token)) return true;
|
|
return false;
|
|
}
|
|
}
|