Passed
Push — master ( 03c468...179943 )
by Darko
11:56
created

MovieCategorizer   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 52
eloc 81
c 1
b 0
f 0
dl 0
loc 207
rs 7.44

14 Methods

Rating   Name   Duplication   Size   Complexity  
B checkUHD() 0 18 8
A checkWebDL() 0 7 2
A checkHD() 0 11 4
A getName() 0 3 1
A checkX265() 0 7 2
A check3D() 0 7 2
A checkDVD() 0 7 2
A looksLikeMovie() 0 3 1
A checkSD() 0 7 2
C categorize() 0 51 14
A checkOther() 0 7 2
A checkForeign() 0 15 4
A checkBluRay() 0 8 3
A shouldSkip() 0 23 5

How to fix   Complexity   

Complex Class

Complex classes like MovieCategorizer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MovieCategorizer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace App\Services\Categorization\Categorizers;
4
5
use App\Models\Category;
6
use App\Services\Categorization\CategorizationResult;
7
use App\Services\Categorization\ReleaseContext;
8
9
/**
10
 * Categorizer for Movie content including HD, SD, UHD, 3D, Blu-ray, DVD, etc.
11
 */
12
class MovieCategorizer extends AbstractCategorizer
13
{
14
    protected int $priority = 25;
15
16
    public function getName(): string
17
    {
18
        return 'Movie';
19
    }
20
21
    public function shouldSkip(ReleaseContext $context): bool
22
    {
23
        // Skip if this looks like adult content
24
        if ($context->hasAdultMarkers()) {
25
            return true;
26
        }
27
28
        // Skip if it looks like a TV episode (S01E01) or season pack (S01.1080p)
29
        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)) {
30
            return true;
31
        }
32
33
        // Skip episode-only patterns (E01, E02) - common in anime
34
        if (preg_match('/[._ -]E\d{1,4}[._ -]/i', $context->releaseName)) {
35
            return true;
36
        }
37
38
        // Skip known anime release groups
39
        if (preg_match('/[.\-_ ](URANiME|ANiHLS|HaiKU|ANiURL|SkyAnime|Erai-raws|LostYears|Vodes|SubsPlease|Judas|Ember|YuiSubs|ASW|Tsundere-Raws|Anime-Raws)[.\-_ ]?/i', $context->releaseName)) {
40
            return true;
41
        }
42
43
        return false;
44
    }
45
46
    public function categorize(ReleaseContext $context): CategorizationResult
47
    {
48
        $name = $context->releaseName;
49
50
        // Check if it looks like movie content
51
        if (!$this->looksLikeMovie($name)) {
52
            return $this->noMatch();
53
        }
54
55
        // Try specific movie subcategories in order of specificity
56
        if ($context->categorizeForeign && ($result = $this->checkForeign($name))) {
57
            return $result;
58
        }
59
60
        if ($result = $this->checkX265($name)) {
61
            return $result;
62
        }
63
64
        if ($result = $this->checkUHD($name)) {
65
            return $result;
66
        }
67
68
        if ($result = $this->check3D($name)) {
69
            return $result;
70
        }
71
72
        if ($result = $this->checkBluRay($name)) {
73
            return $result;
74
        }
75
76
        if ($result = $this->checkDVD($name)) {
77
            return $result;
78
        }
79
80
        if ($context->catWebDL && ($result = $this->checkWebDL($name))) {
81
            return $result;
82
        }
83
84
        if ($result = $this->checkHD($name, $context->catWebDL)) {
85
            return $result;
86
        }
87
88
        if ($result = $this->checkSD($name)) {
89
            return $result;
90
        }
91
92
        if ($result = $this->checkOther($name)) {
93
            return $result;
94
        }
95
96
        return $this->noMatch();
97
    }
98
99
    /**
100
     * Check if release name looks like movie content.
101
     */
102
    protected function looksLikeMovie(string $name): bool
103
    {
104
        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);
105
    }
106
107
    protected function checkForeign(string $name): ?CategorizationResult
108
    {
109
        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)) {
110
            return $this->matched(Category::MOVIE_FOREIGN, 0.8, 'foreign_language');
111
        }
112
113
        if (stripos($name, 'Castellano') !== false) {
114
            return $this->matched(Category::MOVIE_FOREIGN, 0.8, 'foreign_castellano');
115
        }
116
117
        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)) {
118
            return $this->matched(Category::MOVIE_FOREIGN, 0.85, 'foreign_pattern');
119
        }
120
121
        return null;
122
    }
123
124
    protected function checkX265(string $name): ?CategorizationResult
125
    {
126
        if (preg_match('/(\w+[\.-_\s]+).*(x265).*(Tigole|SESKAPiLE|CHD|IAMABLE|THREESOME|OohLaLa|DEFLATE|NCmt)/i', $name)) {
127
            return $this->matched(Category::MOVIE_X265, 0.9, 'x265_group');
128
        }
129
130
        return null;
131
    }
132
133
    protected function checkUHD(string $name): ?CategorizationResult
134
    {
135
        // Skip TV shows
136
        if (preg_match('/(S\d+).*(2160p).*(Netflix|Amazon|NF|AMZN).*(TrollUHD|NTb|VLAD|DEFLATE|CMRG)/i', $name)) {
137
            return null;
138
        }
139
140
        // Check for UHD indicators
141
        if (stripos($name, '2160p') !== false ||
142
            preg_match('/\b(UHD|Ultra[._ -]HD|4K)\b/i', $name) ||
143
            (preg_match('/\b(HDR|HDR10|HDR10\+|Dolby[._ -]?Vision)\b/i', $name) &&
144
             preg_match('/\b(HEVC|H\.?265|x265)\b/i', $name)) ||
145
            (stripos($name, 'UHD') !== false &&
146
             preg_match('/\b(BR|BluRay|Blu[._ -]?Ray)\b/i', $name))) {
147
            return $this->matched(Category::MOVIE_UHD, 0.9, 'uhd');
148
        }
149
150
        return null;
151
    }
152
153
    protected function check3D(string $name): ?CategorizationResult
154
    {
155
        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)) {
156
            return $this->matched(Category::MOVIE_3D, 0.9, '3d');
157
        }
158
159
        return null;
160
    }
161
162
    protected function checkBluRay(string $name): ?CategorizationResult
163
    {
164
        if (preg_match('/bluray-|[._ -]bd?[._ -]?(25|50)|blu-ray|Bluray\s-\sUntouched|[._ -]untouched[._ -]/i', $name) &&
165
            !preg_match('/SecretUsenet\.com$/i', $name)) {
166
            return $this->matched(Category::MOVIE_BLURAY, 0.9, 'bluray');
167
        }
168
169
        return null;
170
    }
171
172
    protected function checkDVD(string $name): ?CategorizationResult
173
    {
174
        if (preg_match('/(dvd\-?r|[._ -]dvd|dvd9|dvd5|[._ -]r5)[._ -]/i', $name)) {
175
            return $this->matched(Category::MOVIE_DVD, 0.85, 'dvd');
176
        }
177
178
        return null;
179
    }
180
181
    protected function checkWebDL(string $name): ?CategorizationResult
182
    {
183
        if (preg_match('/web[._ -]dl|web-?rip/i', $name)) {
184
            return $this->matched(Category::MOVIE_WEBDL, 0.85, 'webdl');
185
        }
186
187
        return null;
188
    }
189
190
    protected function checkHD(string $name, bool $catWebDL): ?CategorizationResult
191
    {
192
        if (preg_match('/720p|1080p|AVC|VC1|VC-1|web-dl|wmvhd|x264|XvidHD|bdrip/i', $name)) {
193
            return $this->matched(Category::MOVIE_HD, 0.85, 'hd');
194
        }
195
196
        if (!$catWebDL && preg_match('/web[._ -]dl|web-?rip/i', $name)) {
197
            return $this->matched(Category::MOVIE_HD, 0.8, 'hd_webdl_fallback');
198
        }
199
200
        return null;
201
    }
202
203
    protected function checkSD(string $name): ?CategorizationResult
204
    {
205
        if (preg_match('/(divx|dvdscr|extrascene|dvdrip|\.CAM|HDTS(-LINE)?|vhsrip|xvid(vd)?)[._ -]/i', $name)) {
206
            return $this->matched(Category::MOVIE_SD, 0.8, 'sd');
207
        }
208
209
        return null;
210
    }
211
212
    protected function checkOther(string $name): ?CategorizationResult
213
    {
214
        if (preg_match('/[._ -]cam[._ -]/i', $name)) {
215
            return $this->matched(Category::MOVIE_OTHER, 0.6, 'cam');
216
        }
217
218
        return null;
219
    }
220
}
221
222