hasAdultMarkers()) { return true; } // Skip if it looks like a TV episode (S01E01) or season pack (S01.1080p) if (preg_match('/[._ -]S\d{1,3}[._ -]?(E\d|D\d|Complete|Full|1080|720|480|2160|WEB|HDTV|BluRay|NF|AMZN)/i', $context->releaseName)) { return true; } // Skip episode-only patterns (E01, E02) - common in anime if (preg_match('/[._ -]E\d{1,4}[._ -]/i', $context->releaseName)) { return true; } // Skip known anime release groups if (preg_match('/(?:^|[.\-_ \[])(URANiME|ANiHLS|HaiKU|ANiURL|SkyAnime|Erai-raws|LostYears|Vodes|SubsPlease|Judas|Ember|YuiSubs|ASW|Tsundere-Raws|Anime-Raws)(?:[.\-_ \]]|$)/i', $context->releaseName)) { return true; } return false; } public function categorize(ReleaseContext $context): CategorizationResult { $name = $context->releaseName; // Check if it looks like movie content if (! $this->looksLikeMovie($name)) { return $this->noMatch(); } // Try specific movie subcategories in order of specificity if ($context->categorizeForeign && ($result = $this->checkForeign($name))) { return $result; } if ($result = $this->checkX265($name)) { return $result; } if ($result = $this->checkUHD($name)) { return $result; } if ($result = $this->check3D($name)) { return $result; } if ($result = $this->checkBluRay($name)) { return $result; } if ($result = $this->checkDVD($name)) { return $result; } if ($context->catWebDL && ($result = $this->checkWebDL($name))) { return $result; } if ($result = $this->checkHD($name, $context->catWebDL)) { return $result; } if ($result = $this->checkSD($name)) { return $result; } if ($result = $this->checkOther($name)) { return $result; } return $this->noMatch(); } /** * Check if release name looks like movie content. */ protected function looksLikeMovie(string $name): bool { return (bool) preg_match('/[._ -]AVC|[BH][DR]RIP|(Bluray|Blu-Ray)|BD[._ -]?(25|50)?|\bBR\b|Camrip|[._ -]\d{4}[._ -].+(720p|1080p|Cam|HDTS|2160p)|DIVX|[._ -]DVD[._ -]|DVD-?(5|9|R|Rip)|Untouched|VHSRip|XVID|[._ -](DTS|TVrip|webrip|WEBDL|WEB-DL)[._ -]|\b(2160)p\b.*\b(Netflix|Amazon|NF|AMZN|Disney)\b/i', $name); } protected function checkForeign(string $name): ?CategorizationResult { if (preg_match('/(danish|flemish|Deutsch|dutch|french|german|heb|hebrew|nl[._ -]?sub|dub(bed|s)?|\.NL|norwegian|swedish|swesub|spanish|Staffel)[._ -]|\(german\)|Multisub/i', $name)) { return $this->matched(Category::MOVIE_FOREIGN, 0.8, 'foreign_language'); } if (stripos($name, 'Castellano') !== false) { return $this->matched(Category::MOVIE_FOREIGN, 0.8, 'foreign_castellano'); } if (preg_match('/(720p|1080p|AC3|AVC|DIVX|DVD(5|9|RIP|R)|XVID)[._ -](Dutch|French|German|ITA)|\(?(Dutch|French|German|ITA)\)?[._ -](720P|1080p|AC3|AVC|DIVX|DVD(5|9|RIP|R)|WEB(-DL|-?RIP)|HD[._ -]|XVID)/i', $name)) { return $this->matched(Category::MOVIE_FOREIGN, 0.85, 'foreign_pattern'); } return null; } protected function checkX265(string $name): ?CategorizationResult { if (preg_match('/(\w+[\.-_\s]+).*(x265).*(Tigole|SESKAPiLE|CHD|IAMABLE|THREESOME|OohLaLa|DEFLATE|NCmt)/i', $name)) { return $this->matched(Category::MOVIE_X265, 0.9, 'x265_group'); } return null; } protected function checkUHD(string $name): ?CategorizationResult { // Skip TV shows if (preg_match('/(S\d+).*(2160p).*(Netflix|Amazon|NF|AMZN).*(TrollUHD|NTb|VLAD|DEFLATE|CMRG)/i', $name)) { return null; } // Check for UHD indicators if (stripos($name, '2160p') !== false || preg_match('/\b(UHD|Ultra[._ -]HD|4K)\b/i', $name) || (preg_match('/\b(HDR|HDR10|HDR10\+|Dolby[._ -]?Vision)\b/i', $name) && preg_match('/\b(HEVC|H\.?265|x265)\b/i', $name)) || (stripos($name, 'UHD') !== false && preg_match('/\b(BR|BluRay|Blu[._ -]?Ray)\b/i', $name))) { return $this->matched(Category::MOVIE_UHD, 0.9, 'uhd'); } return null; } protected function check3D(string $name): ?CategorizationResult { if (preg_match('/[._ -]3D\s?[\.\-_\[ ](1080p|(19|20)\d\d|AVC|BD(25|50)|Blu[._ -]?ray|CEE|Complete|GER|MVC|MULTi|SBS|H(-)?SBS)[._ -]/i', $name)) { return $this->matched(Category::MOVIE_3D, 0.9, '3d'); } return null; } protected function checkBluRay(string $name): ?CategorizationResult { if (preg_match('/bluray-|[._ -]bd?[._ -]?(25|50)|blu-ray|Bluray\s-\sUntouched|[._ -]untouched[._ -]/i', $name) && ! preg_match('/SecretUsenet\.com$/i', $name)) { return $this->matched(Category::MOVIE_BLURAY, 0.9, 'bluray'); } return null; } protected function checkDVD(string $name): ?CategorizationResult { if (preg_match('/(dvd\-?r|[._ -]dvd|dvd9|dvd5|[._ -]r5)[._ -]/i', $name)) { return $this->matched(Category::MOVIE_DVD, 0.85, 'dvd'); } return null; } protected function checkWebDL(string $name): ?CategorizationResult { if (preg_match('/web[._ -]dl|web-?rip/i', $name)) { return $this->matched(Category::MOVIE_WEBDL, 0.85, 'webdl'); } return null; } protected function checkHD(string $name, bool $catWebDL): ?CategorizationResult { if (preg_match('/720p|1080p|AVC|VC1|VC-1|web-dl|wmvhd|x264|XvidHD|bdrip/i', $name)) { return $this->matched(Category::MOVIE_HD, 0.85, 'hd'); } if (! $catWebDL && preg_match('/web[._ -]dl|web-?rip/i', $name)) { return $this->matched(Category::MOVIE_HD, 0.8, 'hd_webdl_fallback'); } return null; } protected function checkSD(string $name): ?CategorizationResult { if (preg_match('/(divx|dvdscr|extrascene|dvdrip|\.CAM|HDTS(-LINE)?|vhsrip|xvid(vd)?)[._ -]/i', $name)) { return $this->matched(Category::MOVIE_SD, 0.8, 'sd'); } return null; } protected function checkOther(string $name): ?CategorizationResult { if (preg_match('/[._ -]cam[._ -]/i', $name)) { return $this->matched(Category::MOVIE_OTHER, 0.6, 'cam'); } return null; } }