Completed
Push — dev ( b9f631...fdb0aa )
by Darko
11:51 queued 04:24
created

ApiController   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 288
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 59
eloc 207
dl 0
loc 288
rs 4.08
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
F api() 0 282 59

How to fix   Complexity   

Complex Class

Complex classes like ApiController 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 ApiController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace App\Http\Controllers\Api;
4
5
use App\Models\User;
6
use App\Models\Release;
7
use Blacklight\http\API;
8
use Blacklight\Releases;
9
use App\Models\ReleaseNfo;
10
use App\Models\UserRequest;
11
use Illuminate\Http\Request;
12
use App\Events\UserAccessedApi;
13
use Blacklight\utility\Utility;
14
use App\Http\Controllers\BasePageController;
15
16
class ApiController extends BasePageController
17
{
18
    /**
19
     * @param \Illuminate\Http\Request $request
20
     * @throws \Throwable
21
     */
22
    public function api(Request $request)
23
    {
24
        // API functions.
25
        $function = 's';
26
        if ($request->has('t')) {
27
            switch ($request->input('t')) {
28
               case 'd':
29
               case 'details':
30
                   $function = 'd';
31
                   break;
32
               case 'g':
33
               case 'get':
34
                   $function = 'g';
35
                   break;
36
               case 's':
37
               case 'search':
38
                   $function = 's';
39
                   break;
40
               case 'c':
41
               case 'caps':
42
                   $function = 'c';
43
                   break;
44
               case 'tv':
45
               case 'tvsearch':
46
                   $function = 'tv';
47
                   break;
48
               case 'm':
49
               case 'movie':
50
                   $function = 'm';
51
                   break;
52
               case 'gn':
53
               case 'n':
54
               case 'nfo':
55
               case 'info':
56
                   $function = 'n';
57
                   break;
58
               default:
59
                   Utility::showApiError(202, 'No such function ('.$request->input('t').')');
60
           }
61
        } else {
62
            Utility::showApiError(200, 'Missing parameter (t)');
63
        }
64
65
        $uid = $apiKey = '';
66
        $res = $catExclusions = [];
67
        $maxRequests = 0;
68
69
        // Page is accessible only by the apikey
70
71
        if ($function !== 'c' && $function !== 'r') {
72
            if (! $request->has('apikey') || ($request->has('apikey') && empty($request->input('apikey')))) {
73
                Utility::showApiError(200, 'Missing parameter (apikey)');
74
            } else {
75
                $apiKey = $request->input('apikey');
76
                $res = User::getByRssToken($apiKey);
77
                if ($res === null) {
78
                    Utility::showApiError(100, 'Incorrect user credentials (wrong API key)');
79
                }
80
            }
81
82
            if ($res->hasRole('Disabled')) {
83
                Utility::showApiError(101);
84
            }
85
86
            $uid = $res['id'];
87
            $catExclusions = User::getCategoryExclusionForApi($request);
88
            $maxRequests = $res->role->apirequests;
0 ignored issues
show
Bug introduced by
The property apirequests does not seem to exist on Spatie\Permission\Models\Role. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
89
        }
90
91
        // Record user access to the api, if its been called by a user (i.e. capabilities request do not require a user to be logged in or key provided).
92
        if ($uid !== '') {
93
            event(new UserAccessedApi($res));
94
            $apiRequests = UserRequest::getApiRequests($uid);
95
            if ($apiRequests > $maxRequests) {
96
                Utility::showApiError(500, 'Request limit reached ('.$apiRequests.'/'.$maxRequests.')');
97
            }
98
        }
99
100
        $releases = new Releases();
101
        $api = new API(['Settings' => $this->settings, 'Request' => $request]);
102
103
        // Set Query Parameters based on Request objects
104
        $outputXML = ! ($request->has('o') && $request->input('o') === 'json');
105
        $minSize = $request->has('minsize') && $request->input('minsize') > 0 ? $request->input('minsize') : 0;
106
        $offset = $api->offset();
107
108
        // Set API Parameters based on Request objects
109
        $params['extended'] = $request->has('extended') && (int) $request->input('extended') === 1 ? '1' : '0';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
110
        $params['del'] = $request->has('del') && (int) $request->input('del') === 1 ? '1' : '0';
111
        $params['uid'] = $uid;
112
        $params['token'] = $apiKey;
113
114
        switch ($function) {
115
           // Search releases.
116
           case 's':
117
               $api->verifyEmptyParameter('q');
118
               $maxAge = $api->maxAge();
119
               $groupName = $api->group();
120
               UserRequest::addApiRequest($apiKey, $request->getRequestUri());
121
               $categoryID = $api->categoryID();
122
               $limit = $api->limit();
123
               $searchArr = [
124
                   'searchname' => $request->input('q') ?? -1,
125
                   'name' => -1,
126
                   'fromname' => -1,
127
                   'filename' => -1,
128
               ];
129
130
               if ($request->has('q')) {
131
                   $relData = $releases->search(
132
                       $searchArr,
133
                       $groupName,
134
                       -1,
135
                       -1,
136
                       -1,
137
                       -1,
138
                       $offset,
139
                       $limit,
140
                       '',
141
                       $maxAge,
142
                       $catExclusions,
143
                       'basic',
144
                       $categoryID,
145
                       $minSize
146
                   );
147
               } else {
148
                   $relData = $releases->getBrowseRange(
149
                       1,
150
                       $categoryID,
151
                       $offset,
152
                       $limit,
153
                       '',
154
                       $maxAge,
155
                       $catExclusions,
156
                       $groupName,
0 ignored issues
show
Bug introduced by
It seems like $groupName can also be of type string and true; however, parameter $groupName of Blacklight\Releases::getBrowseRange() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
                       /** @scrutinizer ignore-type */ $groupName,
Loading history...
157
                       $minSize
158
                   );
159
               }
160
               $api->output($relData, $params, $outputXML, $offset, 'api');
161
               break;
162
           // Search tv releases.
163
           case 'tv':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
164
               $api->verifyEmptyParameter('q');
165
               $api->verifyEmptyParameter('vid');
166
               $api->verifyEmptyParameter('tvdbid');
167
               $api->verifyEmptyParameter('traktid');
168
               $api->verifyEmptyParameter('rid');
169
               $api->verifyEmptyParameter('tvmazeid');
170
               $api->verifyEmptyParameter('imdbid');
171
               $api->verifyEmptyParameter('tmdbid');
172
               $api->verifyEmptyParameter('season');
173
               $api->verifyEmptyParameter('ep');
174
               $maxAge = $api->maxAge();
175
               UserRequest::addApiRequest($apiKey, $request->getRequestUri());
176
177
               $siteIdArr = [
178
                   'id'     => $request->input('vid') ?? '0',
179
                   'tvdb'   => $request->input('tvdbid') ?? '0',
180
                   'trakt'  => $request->input('traktid') ?? '0',
181
                   'tvrage' => $request->input('rid') ?? '0',
182
                   'tvmaze' => $request->input('tvmazeid') ?? '0',
183
                   'imdb'   => $request->input('imdbid') ?? '0',
184
                   'tmdb'   => $request->input('tmdbid') ?? '0',
185
               ];
186
187
               // Process season only queries or Season and Episode/Airdate queries
188
189
               $series = $request->input('season') ?? '';
190
               $episode = $request->input('ep') ?? '';
191
192
               if (preg_match('#^(19|20)\d{2}$#', $series, $year) && strpos($episode, '/') !== false) {
193
                   $airdate = str_replace('/', '-', $year[0].'-'.$episode);
194
               }
195
196
               $relData = $releases->tvSearch(
197
                   $siteIdArr,
198
                   $series,
199
                   $episode,
200
                   $airdate ?? '',
201
                   $api->offset(),
202
                   $api->limit(),
203
                   $request->input('q') ?? '',
204
                   $api->categoryID(),
205
                   $maxAge,
206
                   $minSize,
207
                   $catExclusions
208
               );
209
210
               $api->addLanguage($relData);
0 ignored issues
show
Bug introduced by
It seems like $relData can also be of type Illuminate\Database\Eloquent\Collection; however, parameter $releases of Blacklight\http\API::addLanguage() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

210
               $api->addLanguage(/** @scrutinizer ignore-type */ $relData);
Loading history...
211
               $api->output($relData, $params, $outputXML, $offset, 'api');
212
               break;
213
214
           // Search movie releases.
215
           case 'm':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
216
               $api->verifyEmptyParameter('q');
217
               $api->verifyEmptyParameter('imdbid');
218
               $maxAge = $api->maxAge();
219
               UserRequest::addApiRequest($apiKey, $request->getRequestUri());
220
221
               $imdbId = $request->has('imdbid') && ! empty($request->input('imdbid')) ? $request->input('imdbid') : -1;
222
               $tmdbId = $request->has('tmdbid') && ! empty($request->input('tmdbid')) ? $request->input('tmdbid') : -1;
223
               $traktId = $request->has('traktid') && ! empty($request->input('traktid')) ? $request->input('traktid') : -1;
224
225
               $relData = $releases->moviesSearch(
226
                   $imdbId,
227
                   $tmdbId,
228
                   $traktId,
229
                   $api->offset(),
230
                   $api->limit(),
231
                   $request->input('q') ?? '',
232
                   $api->categoryID(),
233
                   $maxAge,
234
                   $minSize,
235
                   $catExclusions
236
               );
237
238
               $api->addCoverURL(
239
                   $relData,
240
                   function ($release) {
241
                       return Utility::getCoverURL(['type' => 'movies', 'id' => $release->imdbid]);
242
                   }
243
               );
244
245
               $api->addLanguage($relData);
246
               $api->output($relData, $params, $outputXML, $offset, 'api');
247
               break;
248
249
           // Get NZB.
250
           case 'g':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
251
               $api->verifyEmptyParameter('g');
252
               UserRequest::addApiRequest($apiKey, $request->getRequestUri());
253
               $relData = Release::checkGuidForApi($request->input('id'));
254
               if ($relData) {
255
                   return redirect(WWW_TOP.'/getnzb?r='.$apiKey.'&id='.$request->input('id').(($request->has('del') && $request->input('del') === '1') ? '&del=1' : ''));
0 ignored issues
show
Bug introduced by
The constant App\Http\Controllers\Api\WWW_TOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
256
               }
257
258
               Utility::showApiError(300, 'No such item (the guid you provided has no release in our database)');
259
               break;
260
261
           // Get individual NZB details.
262
           case 'd':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
263
               if (! $request->has('id')) {
264
                   Utility::showApiError(200, 'Missing parameter (guid is required for single release details)');
265
               }
266
267
               UserRequest::addApiRequest($apiKey, $request->getRequestUri());
268
               $data = Release::getByGuid($request->input('id'));
269
270
               $api->output($data, $params, $outputXML, $offset, 'api');
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type App\Models\Release and Illuminate\Database\Eloquent\Model; however, parameter $data of Blacklight\http\Capabilities::output() does only seem to accept Illuminate\Database\Eloquent\Collection|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

270
               $api->output(/** @scrutinizer ignore-type */ $data, $params, $outputXML, $offset, 'api');
Loading history...
271
               break;
272
273
           // Get an NFO file for an individual release.
274
           case 'n':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
275
               if (! $request->has('id')) {
276
                   Utility::showApiError(200, 'Missing parameter (id is required for retrieving an NFO)');
277
               }
278
279
               UserRequest::addApiRequest($apiKey, $request->getRequestUri());
280
               $rel = Release::query()->where('guid', $request->input('id'))->first(['id', 'searchname']);
281
               $data = ReleaseNfo::getReleaseNfo($rel['id']);
282
283
               if ($rel !== null) {
284
                   if ($data !== null) {
285
                       if ($request->has('o') && $request->input('o') === 'file') {
286
                           return response()->streamDownload(function () use ($data) {
287
                               echo $data['nfo'];
288
                           }, $rel['searchname'].'.nfo', ['Content-type:' => 'application/octet-stream']);
289
                       }
290
291
                       echo nl2br(Utility::cp437toUTF($data['nfo']));
292
                   } else {
293
                       Utility::showApiError(300, 'Release does not have an NFO file associated.');
294
                   }
295
               } else {
296
                   Utility::showApiError(300, 'Release does not exist.');
297
               }
298
               break;
299
300
           // Capabilities request.
301
           case 'c':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
302
               $api->output([], $params, $outputXML, $offset, 'caps');
303
               break;
304
       }
305
    }
306
}
307