using System.Text.RegularExpressions; namespace Parser.Core; public static class QualityParser { private static readonly Regex SourceRegex = new(@"\b(?: (?M?Blu[-_. ]?Ray|HD[-_. ]?DVD|BD(?!$)|UHD2?BD|BDISO|BDMux|BD25|BD50|BR[-_. ]?DISK)| (?WEB[-_. ]?DL(?:mux)?|AmazonHD|AmazonSD|iTunesHD|MaxdomeHD|NetflixU?HD|WebHD|HBOMaxHD|DisneyHD|[. ]WEB[. ](?:[xh][ .]?26[45]|AVC|HEVC|DDP?5[. ]1)|[. ](?-i:WEB)$|(?:\d{3,4}0p)[-. ](?:Hybrid[-_. ]?)?WEB[-. ]|[-. ]WEB[-. ]\d{3,4}0p|\b\s\/\sWEB\s\/\s\b|(?:AMZN|NF|DP)[. -]WEB[. -](?!Rip))| (?WebRip|Web-Rip|WEBMux)| (?HDTV)| (?BDRip|BDLight|HD[-_. ]?DVDRip|UHDBDRip)| (?BRRip)| (?\d?x?M?DVD-?[R59])| (?DVD(?!-R)|DVDRip|xvidvd)| (?WS[-_. ]DSR|DSR)| (?R[0-9]{1}|REGIONAL)| (?SCR|SCREENER|DVDSCR|DVDSCREENER)| (?TS[-_. ]|TELESYNCH?|HD-TS|HDTS|PDVD|TSRip|HDTSRip)| (?TC|TELECINE|HD-TC|HDTC)| (?CAMRIP|(?:NEW)?CAM|HD-?CAM(?:Rip)?|HQCAM)| (?WORKPRINT|WP)| (?PDTV)| (?SDTV)| (?TVRip) )(?:\b|$|[ .])", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); private static readonly Regex ResolutionRegex = new( @"\b(?:(?360p)|(?480p|480i|640x480|848x480)|(?540p)|(?576p)|(?720p|1280x720|960p)|(?1080p|1920x1080|1440p|FHD|1080i|4kto1080p)|(?2160p|3840x2160|4k[-_. ](?:UHD|HEVC|BD|H\.?265)|(?:UHD|HEVC|BD|H\.?265)[-_. ]4k))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex AlternativeResolutionRegex = new( @"\b(?UHD)\b|(?\[4K\])", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex RemuxRegex = new( @"(?:[_. \[]|\d{4}p-|\bHybrid-)(?(?:(BD|UHD)[-_. ]?)?Remux)\b|(?(?:(BD|UHD)[-_. ]?)?Remux[_. ]\d{4}p)", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex ProperRegex = new(@"\b(?proper)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex RepackRegex = new(@"\b(?repack\d?|rerip\d?)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex VersionRegex = new( @"\d[-._ ]?v(?\d)[-._ ]|\[v(?\d)\]|repack(?\d)|rerip(?\d)", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex RealRegex = new(@"\b(?REAL)\b", RegexOptions.Compiled); private static readonly Regex RawHDRegex = new(@"\b(?RawHD|Raw[-_. ]HD)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex BRDISKRegex = new( @"^(?!.*\b((?x264)|(?h264)|(?XvidHD)|(?X-?vid)|(?divx))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex AnimeBlurayRegex = new( @"bd(?:720|1080|2160)|(?<=[-_. (\[])bd(?=[-_. )\]])", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex AnimeWebDlRegex = new( @"\[WEB\]|[\[\(]WEB[ .]", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex MPEG2Regex = new(@"\b(?MPEG[-_. ]?2)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); public static QualityResult ParseQuality(string name) { var normalizedName = name.Replace('_', ' ').Trim(); var result = new QualityResult(); // Parse revision/modifiers ParseRevision(name, normalizedName, result); // Parse resolution var resolution = ParseResolution(normalizedName); result.Resolution = resolution; // Check RawHD if (RawHDRegex.IsMatch(normalizedName) && !BRDISKRegex.IsMatch(normalizedName)) { result.Modifier = QualityModifier.RawHD; return result; } // Check source var sourceMatch = SourceRegex.Match(normalizedName); var isRemux = RemuxRegex.IsMatch(normalizedName); var isBRDisk = BRDISKRegex.IsMatch(normalizedName); var codecMatch = CodecRegex.Match(normalizedName); if (sourceMatch.Success) { if (sourceMatch.Groups["bluray"].Success) { result.Source = QualitySource.Bluray; if (isBRDisk) { result.Modifier = QualityModifier.BRDisk; return result; } if (codecMatch.Groups["xvid"].Success || codecMatch.Groups["divx"].Success) { result.Resolution = Resolution.R480p; return result; } result.Modifier = isRemux ? QualityModifier.Remux : QualityModifier.None; if (result.Resolution == Resolution.Unknown) result.Resolution = Resolution.R720p; return result; } if (sourceMatch.Groups["webdl"].Success) { result.Source = QualitySource.WebDL; if (result.Resolution == Resolution.Unknown) result.Resolution = Resolution.R480p; return result; } if (sourceMatch.Groups["webrip"].Success) { result.Source = QualitySource.WebRip; if (result.Resolution == Resolution.Unknown) result.Resolution = Resolution.R480p; return result; } if (sourceMatch.Groups["hdtv"].Success) { result.Source = QualitySource.TV; if (MPEG2Regex.IsMatch(normalizedName)) { result.Modifier = QualityModifier.RawHD; } return result; } if (sourceMatch.Groups["bdrip"].Success || sourceMatch.Groups["brrip"].Success) { result.Source = QualitySource.Bluray; if (result.Resolution == Resolution.Unknown) result.Resolution = Resolution.R480p; return result; } if (sourceMatch.Groups["dvdr"].Success || sourceMatch.Groups["dvd"].Success) { result.Source = QualitySource.DVD; result.Resolution = Resolution.R480p; return result; } if (sourceMatch.Groups["scr"].Success) { result.Source = QualitySource.DVD; result.Resolution = Resolution.R480p; result.Modifier = QualityModifier.Screener; return result; } if (sourceMatch.Groups["cam"].Success) { result.Source = QualitySource.Cam; return result; } if (sourceMatch.Groups["ts"].Success) { result.Source = QualitySource.Telesync; return result; } if (sourceMatch.Groups["tc"].Success) { result.Source = QualitySource.Telecine; return result; } if (sourceMatch.Groups["wp"].Success) { result.Source = QualitySource.Workprint; return result; } if (sourceMatch.Groups["regional"].Success) { result.Source = QualitySource.DVD; result.Resolution = Resolution.R480p; result.Modifier = QualityModifier.Regional; return result; } if (sourceMatch.Groups["pdtv"].Success || sourceMatch.Groups["sdtv"].Success || sourceMatch.Groups["dsr"].Success || sourceMatch.Groups["tvrip"].Success) { result.Source = QualitySource.TV; return result; } } // No source - check remux with resolution if (isRemux && resolution != Resolution.Unknown) { result.Source = QualitySource.Bluray; result.Modifier = QualityModifier.Remux; return result; } // Anime patterns if (AnimeBlurayRegex.IsMatch(normalizedName)) { result.Source = QualitySource.Bluray; result.Modifier = isRemux ? QualityModifier.Remux : QualityModifier.None; if (result.Resolution == Resolution.Unknown) result.Resolution = Resolution.R720p; return result; } if (AnimeWebDlRegex.IsMatch(normalizedName)) { result.Source = QualitySource.WebDL; if (result.Resolution == Resolution.Unknown) result.Resolution = Resolution.R720p; return result; } // Resolution only if (resolution != Resolution.Unknown && isRemux) { result.Source = QualitySource.Bluray; result.Modifier = QualityModifier.Remux; } return result; } private static Resolution ParseResolution(string name) { var match = ResolutionRegex.Match(name); var altMatch = AlternativeResolutionRegex.Match(name); if (!match.Success && !altMatch.Success) return Resolution.Unknown; if (match.Groups["R360p"].Success) return Resolution.R360p; if (match.Groups["R480p"].Success) return Resolution.R480p; if (match.Groups["R540p"].Success) return Resolution.R540p; if (match.Groups["R576p"].Success) return Resolution.R576p; if (match.Groups["R720p"].Success) return Resolution.R720p; if (match.Groups["R1080p"].Success) return Resolution.R1080p; if (match.Groups["R2160p"].Success || altMatch.Groups["R2160p"].Success) return Resolution.R2160p; return Resolution.Unknown; } private static void ParseRevision(string name, string normalizedName, QualityResult result) { var versionMatch = VersionRegex.Match(normalizedName); if (versionMatch.Success && versionMatch.Groups["version"].Success) { result.Revision.Version = int.Parse(versionMatch.Groups["version"].Value); } if (ProperRegex.IsMatch(normalizedName)) { result.Revision.Version = versionMatch.Success ? result.Revision.Version + 1 : 2; } if (RepackRegex.IsMatch(normalizedName)) { result.Revision.Version = versionMatch.Success ? result.Revision.Version + 1 : 2; result.Revision.IsRepack = true; } var realMatches = RealRegex.Matches(name); if (realMatches.Count > 0) { result.Revision.Real = realMatches.Count; } } }