mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
- Added RegexReplace class for handling regex replacements. - Created ReleaseGroupParser for extracting release groups from titles. - Developed TitleParser for parsing movie titles, including editions and IDs. - Introduced QualitySource, Resolution, QualityModifier enums and QualityResult class for quality metadata. - Set up Dockerfile and docker-compose for containerized deployment. - Implemented ASP.NET Core web API for parsing requests. - Added TypeScript client for interacting with the parser service. - Enhanced configuration to support dynamic parser service URL.
294 lines
11 KiB
C#
294 lines
11 KiB
C#
using System.Text.RegularExpressions;
|
|
|
|
namespace Parser.Core;
|
|
|
|
public static class QualityParser
|
|
{
|
|
private static readonly Regex SourceRegex = new(@"\b(?:
|
|
(?<bluray>M?Blu[-_. ]?Ray|HD[-_. ]?DVD|BD(?!$)|UHD2?BD|BDISO|BDMux|BD25|BD50|BR[-_. ]?DISK)|
|
|
(?<webdl>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>WebRip|Web-Rip|WEBMux)|
|
|
(?<hdtv>HDTV)|
|
|
(?<bdrip>BDRip|BDLight|HD[-_. ]?DVDRip|UHDBDRip)|
|
|
(?<brrip>BRRip)|
|
|
(?<dvdr>\d?x?M?DVD-?[R59])|
|
|
(?<dvd>DVD(?!-R)|DVDRip|xvidvd)|
|
|
(?<dsr>WS[-_. ]DSR|DSR)|
|
|
(?<regional>R[0-9]{1}|REGIONAL)|
|
|
(?<scr>SCR|SCREENER|DVDSCR|DVDSCREENER)|
|
|
(?<ts>TS[-_. ]|TELESYNCH?|HD-TS|HDTS|PDVD|TSRip|HDTSRip)|
|
|
(?<tc>TC|TELECINE|HD-TC|HDTC)|
|
|
(?<cam>CAMRIP|(?:NEW)?CAM|HD-?CAM(?:Rip)?|HQCAM)|
|
|
(?<wp>WORKPRINT|WP)|
|
|
(?<pdtv>PDTV)|
|
|
(?<sdtv>SDTV)|
|
|
(?<tvrip>TVRip)
|
|
)(?:\b|$|[ .])",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
|
|
|
private static readonly Regex ResolutionRegex = new(
|
|
@"\b(?:(?<R360p>360p)|(?<R480p>480p|480i|640x480|848x480)|(?<R540p>540p)|(?<R576p>576p)|(?<R720p>720p|1280x720|960p)|(?<R1080p>1080p|1920x1080|1440p|FHD|1080i|4kto1080p)|(?<R2160p>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(?<R2160p>UHD)\b|(?<R2160p>\[4K\])",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
private static readonly Regex RemuxRegex = new(
|
|
@"(?:[_. \[]|\d{4}p-|\bHybrid-)(?<remux>(?:(BD|UHD)[-_. ]?)?Remux)\b|(?<remux>(?:(BD|UHD)[-_. ]?)?Remux[_. ]\d{4}p)",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
private static readonly Regex ProperRegex = new(@"\b(?<proper>proper)\b",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
private static readonly Regex RepackRegex = new(@"\b(?<repack>repack\d?|rerip\d?)\b",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
private static readonly Regex VersionRegex = new(
|
|
@"\d[-._ ]?v(?<version>\d)[-._ ]|\[v(?<version>\d)\]|repack(?<version>\d)|rerip(?<version>\d)",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
private static readonly Regex RealRegex = new(@"\b(?<real>REAL)\b", RegexOptions.Compiled);
|
|
|
|
private static readonly Regex RawHDRegex = new(@"\b(?<rawhd>RawHD|Raw[-_. ]HD)\b",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
private static readonly Regex BRDISKRegex = new(
|
|
@"^(?!.*\b((?<!HD[._ -]|HD)DVD|BDRip|720p|MKV|XviD|WMV|d3g|(BD)?REMUX|^(?=.*1080p)(?=.*HEVC)|[xh][-_. ]?26[45]|German.*[DM]L|((?<=\d{4}).*German.*([DM]L)?)(?=.*\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2)\b))\b)(((?=.*\b(Blu[-_. ]?ray|BD|HD[-_. ]?DVD)\b)(?=.*\b(AVC|HEVC|VC[-_. ]?1|MVC|MPEG[-_. ]?2|BDMV|ISO)\b))|^((?=.*\b(((?=.*\b((.*_)?COMPLETE.*|Dis[ck])\b)(?=.*(Blu[-_. ]?ray|HD[-_. ]?DVD)))|3D[-_. ]?BD|BR[-_. ]?DISK|Full[-_. ]?Blu[-_. ]?ray|^((?=.*((BD|UHD)[-_. ]?(25|50|66|100|ISO)))))))).*",
|
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
private static readonly Regex CodecRegex = new(
|
|
@"\b(?:(?<x264>x264)|(?<h264>h264)|(?<xvidhd>XvidHD)|(?<xvid>X-?vid)|(?<divx>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(?<mpeg2>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;
|
|
}
|
|
}
|
|
}
|