Passed
Push — master ( 0048d6...5c61f0 )
by Darko
03:08
created

Releases::animeSearch()   B

Complexity

Conditions 11
Paths 15

Size

Total Lines 62
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 5 Features 0
Metric Value
eloc 48
c 10
b 5
f 0
dl 0
loc 62
rs 7.3166
cc 11
nc 15
nop 7

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Blacklight;
4
5
use App\Models\Category;
6
use App\Models\Release;
7
use App\Models\Settings;
8
use App\Models\UsenetGroup;
9
use Elasticsearch;
10
use Elasticsearch\Common\Exceptions\Missing404Exception;
11
use Illuminate\Database\Eloquent\Collection;
12
use Illuminate\Support\Arr;
13
use Illuminate\Support\Facades\Cache;
14
use Illuminate\Support\Facades\DB;
15
use Illuminate\Support\Facades\File;
16
17
/**
18
 * Class Releases.
19
 */
20
class Releases extends Release
21
{
22
    // RAR/ZIP Password indicator.
23
    public const PASSWD_NONE = 0; // No password.
24
25
    public const PASSWD_RAR = 1; // Definitely passworded.
26
27
    public int $passwordStatus;
28
29
    private ManticoreSearch $manticoreSearch;
30
31
    private ElasticSearchSiteSearch $elasticSearch;
32
33
    /**
34
     * @var array Class instances.
35
     *
36
     * @throws \Exception
37
     */
38
    public function __construct()
39
    {
40
        parent::__construct();
41
        $this->manticoreSearch = new ManticoreSearch;
42
        $this->elasticSearch = new ElasticSearchSiteSearch;
43
    }
44
45
    /**
46
     * Used for Browse results.
47
     *
48
     *
49
     * @return Collection|mixed
50
     */
51
    public function getBrowseRange($page, $cat, $start, $num, $orderBy, int $maxAge = -1, array $excludedCats = [], int|string $groupName = -1, int $minSize = 0): mixed
52
    {
53
        $page = max(1, $page);
54
        $start = max(0, $start);
55
56
        $orderBy = $this->getBrowseOrder($orderBy);
57
58
        $qry = sprintf(
59
            "SELECT r.id, r.searchname, r.groups_id, r.guid, r.postdate, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus, cp.title AS parent_category, c.title AS sub_category, g.name as group_name,
60
        CONCAT(cp.title, ' > ', c.title) AS category_name,
61
        CONCAT(cp.id, ',', c.id) AS category_ids,
62
        df.failed AS failed,
63
        rn.releases_id AS nfoid,
64
        re.releases_id AS reid,
65
        v.tvdb, v.trakt, v.tvrage, v.tvmaze, v.imdb, v.tmdb,
66
        tve.title, tve.firstaired
67
    FROM releases r
68
    LEFT JOIN usenet_groups g ON g.id = r.groups_id
69
    LEFT JOIN categories c ON c.id = r.categories_id
70
    LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
71
    LEFT OUTER JOIN videos v ON r.videos_id = v.id
72
    LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
73
    LEFT OUTER JOIN video_data re ON re.releases_id = r.id
74
    LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
75
    LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
76
    WHERE r.nzbstatus = %d
77
    AND r.passwordstatus %s
78
    %s %s %s %s %s
79
    GROUP BY r.id
80
    ORDER BY %s %s
81
    LIMIT %d OFFSET %d",
82
            NZB::NZB_ADDED,
83
            $this->showPasswords(),
84
            Category::getCategorySearch($cat),
0 ignored issues
show
Bug introduced by
It seems like App\Models\Category::getCategorySearch($cat) can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

84
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat),
Loading history...
85
            ($maxAge > 0 ? (' AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ') : ''),
86
            (\count($excludedCats) ? (' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')') : ''),
87
            ((int) $groupName !== -1 ? sprintf(' AND g.name = %s ', escapeString($groupName)) : ''),
88
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''),
89
            $orderBy[0],
90
            $orderBy[1],
91
            $num,
92
            $start
93
        );
94
95
        $releases = Cache::get(md5($qry.$page));
96
        if ($releases !== null) {
97
            return $releases;
98
        }
99
        $sql = self::fromQuery($qry);
100
        if (\count($sql) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $sql can also be of type Illuminate\Database\Eloq...gHasThroughRelationship; however, parameter $value of count() does only seem to accept Countable|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

100
        if (\count(/** @scrutinizer ignore-type */ $sql) > 0) {
Loading history...
101
            $possibleRows = $this->getBrowseCount($cat, $maxAge, $excludedCats, $groupName);
102
            $sql[0]->_totalcount = $sql[0]->_totalrows = $possibleRows;
103
        }
104
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
105
        Cache::put(md5($qry.$page), $sql, $expiresAt);
106
107
        return $sql;
108
    }
109
110
    /**
111
     * Used for pager on browse page.
112
     */
113
    public function getBrowseCount(array $cat, int $maxAge = -1, array $excludedCats = [], int|string $groupName = ''): int
114
    {
115
        return $this->getPagerCount(sprintf(
116
            'SELECT COUNT(r.id) AS count
117
				FROM releases r
118
				%s
119
				WHERE r.nzbstatus = %d
120
				AND r.passwordstatus %s
121
				%s
122
				%s %s %s ',
123
            ($groupName !== -1 ? 'LEFT JOIN usenet_groups g ON g.id = r.groups_id' : ''),
124
            NZB::NZB_ADDED,
125
            $this->showPasswords(),
126
            ($groupName !== -1 ? sprintf(' AND g.name = %s', escapeString($groupName)) : ''),
127
            Category::getCategorySearch($cat),
0 ignored issues
show
Bug introduced by
It seems like App\Models\Category::getCategorySearch($cat) can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

127
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat),
Loading history...
128
            ($maxAge > 0 ? (' AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ') : ''),
129
            (\count($excludedCats) ? (' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')') : '')
130
        ));
131
    }
132
133
    public function showPasswords(): string
134
    {
135
        $show = (int) Settings::settingValue('showpasswordedrelease');
136
        $setting = $show ?? 0;
137
138
        return match ($setting) {
139
            1 => '<= '.self::PASSWD_RAR,
140
            default => '= '.self::PASSWD_NONE,
141
        };
142
    }
143
144
    /**
145
     * Use to order releases on site.
146
     */
147
    public function getBrowseOrder(array|string $orderBy): array
148
    {
149
        $orderArr = explode('_', ($orderBy === '' ? 'posted_desc' : $orderBy));
0 ignored issues
show
Bug introduced by
$orderBy === '' ? 'posted_desc' : $orderBy of type array is incompatible with the type string expected by parameter $string of explode(). ( Ignorable by Annotation )

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

149
        $orderArr = explode('_', /** @scrutinizer ignore-type */ ($orderBy === '' ? 'posted_desc' : $orderBy));
Loading history...
introduced by
The condition $orderBy === '' is always false.
Loading history...
150
        $orderField = match ($orderArr[0]) {
151
            'cat' => 'categories_id',
152
            'name' => 'searchname',
153
            'size' => 'size',
154
            'files' => 'totalpart',
155
            'stats' => 'grabs',
156
            default => 'postdate',
157
        };
158
159
        return [$orderField, isset($orderArr[1]) && preg_match('/^(asc|desc)$/i', $orderArr[1]) ? $orderArr[1] : 'desc'];
160
    }
161
162
    /**
163
     * Return ordering types usable on site.
164
     *
165
     * @return string[]
166
     */
167
    public function getBrowseOrdering(): array
168
    {
169
        return [
170
            'name_asc',
171
            'name_desc',
172
            'cat_asc',
173
            'cat_desc',
174
            'posted_asc',
175
            'posted_desc',
176
            'size_asc',
177
            'size_desc',
178
            'files_asc',
179
            'files_desc',
180
            'stats_asc',
181
            'stats_desc',
182
        ];
183
    }
184
185
    /**
186
     * @return Release[]|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Query\Builder[]|\Illuminate\Support\Collection
187
     */
188
    public function getForExport(string $postFrom = '', string $postTo = '', string $groupID = '')
189
    {
190
        $query = self::query()
191
            ->where('r.nzbstatus', NZB::NZB_ADDED)
192
            ->select(['r.searchname', 'r.guid', 'g.name as gname', DB::raw("CONCAT(cp.title,'_',c.title) AS catName")])
193
            ->from('releases as r')
0 ignored issues
show
Bug introduced by
'releases as r' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $table of Illuminate\Database\Query\Builder::from(). ( Ignorable by Annotation )

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

193
            ->from(/** @scrutinizer ignore-type */ 'releases as r')
Loading history...
194
            ->leftJoin('categories as c', 'c.id', '=', 'r.categories_id')
195
            ->leftJoin('root_categories as cp', 'cp.id', '=', 'c.root_categories_id')
196
            ->leftJoin('usenet_groups as g', 'g.id', '=', 'r.groups_id');
197
198
        if ($groupID !== '') {
199
            $query->where('r.groups_id', $groupID);
200
        }
201
202
        if ($postFrom !== '') {
203
            $dateParts = explode('/', $postFrom);
204
            if (\count($dateParts) === 3) {
205
                $query->where('r.postdate', '>', $dateParts[2].'-'.$dateParts[1].'-'.$dateParts[0].'00:00:00');
206
            }
207
        }
208
209
        if ($postTo !== '') {
210
            $dateParts = explode('/', $postTo);
211
            if (\count($dateParts) === 3) {
212
                $query->where('r.postdate', '<', $dateParts[2].'-'.$dateParts[1].'-'.$dateParts[0].'23:59:59');
213
            }
214
        }
215
216
        return $query->get();
217
    }
218
219
    /**
220
     * @return mixed|string
221
     */
222
    public function getEarliestUsenetPostDate(): mixed
223
    {
224
        $row = self::query()->selectRaw("DATE_FORMAT(min(postdate), '%d/%m/%Y') AS postdate")->first();
225
226
        return $row === null ? '01/01/2014' : $row['postdate'];
227
    }
228
229
    /**
230
     * @return mixed|string
231
     */
232
    public function getLatestUsenetPostDate(): mixed
233
    {
234
        $row = self::query()->selectRaw("DATE_FORMAT(max(postdate), '%d/%m/%Y') AS postdate")->first();
235
236
        return $row === null ? '01/01/2014' : $row['postdate'];
237
    }
238
239
    public function getReleasedGroupsForSelect(bool $blnIncludeAll = true): array
240
    {
241
        $groups = self::query()
242
            ->selectRaw('DISTINCT g.id, g.name')
243
            ->leftJoin('usenet_groups as g', 'g.id', '=', 'releases.groups_id')
244
            ->get();
245
        $temp_array = [];
246
247
        if ($blnIncludeAll) {
248
            $temp_array[-1] = '--All Groups--';
249
        }
250
251
        foreach ($groups as $group) {
252
            $temp_array[$group['id']] = $group['name'];
253
        }
254
255
        return $temp_array;
256
    }
257
258
    /**
259
     * @return \Illuminate\Cache\|\Illuminate\Database\Eloquent\Collection|mixed
260
     */
261
    public function getShowsRange($userShows, $offset, $limit, $orderBy, int $maxAge = -1, array $excludedCats = [])
262
    {
263
        $orderBy = $this->getBrowseOrder($orderBy);
264
        $sql = sprintf(
265
            "SELECT r.id, r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus,  cp.title AS parent_category, c.title AS sub_category,
266
					CONCAT(cp.title, '->', c.title) AS category_name
267
				FROM releases r
268
				LEFT JOIN categories c ON c.id = r.categories_id
269
				LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
270
				WHERE %s %s
271
				AND r.nzbstatus = %d
272
				AND r.categories_id BETWEEN %d AND %d
273
				AND r.passwordstatus %s
274
				%s
275
				GROUP BY r.id
276
				ORDER BY %s %s %s",
277
            $this->uSQL($userShows, 'videos_id'),
278
            (! empty($excludedCats) ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
279
            NZB::NZB_ADDED,
280
            Category::TV_ROOT,
281
            Category::TV_OTHER,
282
            $this->showPasswords(),
283
            ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : ''),
284
            $orderBy[0],
285
            $orderBy[1],
286
            ($offset === false ? '' : (' LIMIT '.$limit.' OFFSET '.$offset))
287
        );
288
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long'));
289
        $result = Cache::get(md5($sql));
290
        if ($result !== null) {
291
            return $result;
292
        }
293
        $result = self::fromQuery($sql);
294
        Cache::put(md5($sql), $result, $expiresAt);
295
296
        return $result;
297
    }
298
299
    public function getShowsCount($userShows, int $maxAge = -1, array $excludedCats = []): int
300
    {
301
        return $this->getPagerCount(
302
            sprintf(
303
                'SELECT r.id
304
				FROM releases r
305
				WHERE %s %s
306
				AND r.nzbstatus = %d
307
				AND r.categories_id BETWEEN %d AND %d
308
				AND r.passwordstatus %s
309
				%s',
310
                $this->uSQL($userShows, 'videos_id'),
311
                (\count($excludedCats) ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
312
                NZB::NZB_ADDED,
313
                Category::TV_ROOT,
314
                Category::TV_OTHER,
315
                $this->showPasswords(),
316
                ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '')
317
            )
318
        );
319
    }
320
321
    /**
322
     * @throws \Exception
323
     */
324
    public function deleteMultiple(int|array|string $list): void
325
    {
326
        $list = (array) $list;
327
328
        $nzb = new NZB;
329
        $releaseImage = new ReleaseImage;
330
331
        foreach ($list as $identifier) {
332
            $this->deleteSingle(['g' => $identifier, 'i' => false], $nzb, $releaseImage);
333
        }
334
    }
335
336
    /**
337
     * Deletes a single release by GUID, and all the corresponding files.
338
     *
339
     * @param  array  $identifiers  ['g' => Release GUID(mandatory), 'id => ReleaseID(optional, pass
340
     *                              false)]
341
     *
342
     * @throws \Exception
343
     */
344
    public function deleteSingle(array $identifiers, NZB $nzb, ReleaseImage $releaseImage): void
345
    {
346
        // Delete NZB from disk.
347
        $nzbPath = $nzb->NZBPath($identifiers['g']);
348
        if (! empty($nzbPath)) {
349
            File::delete($nzbPath);
350
        }
351
352
        // Delete images.
353
        $releaseImage->delete($identifiers['g']);
354
355
        if (config('nntmux.elasticsearch_enabled') === true) {
356
            if ($identifiers['i'] === false) {
357
                $identifiers['i'] = Release::query()->where('guid', $identifiers['g'])->first(['id']);
358
                if ($identifiers['i'] !== null) {
359
                    $identifiers['i'] = $identifiers['i']['id'];
360
                }
361
            }
362
            if ($identifiers['i'] !== null) {
363
                $params = [
364
                    'index' => 'releases',
365
                    'id' => $identifiers['i'],
366
                ];
367
368
                try {
369
                    Elasticsearch::delete($params);
370
                } catch (Missing404Exception $e) {
371
                    // we do nothing here just catch the error, we don't care if release is missing from ES, we are deleting it anyway
372
                }
373
            }
374
        } else {
375
            // Delete from sphinx.
376
            $this->manticoreSearch->deleteRelease($identifiers);
377
        }
378
379
        // Delete from DB.
380
        self::whereGuid($identifiers['g'])->delete();
381
    }
382
383
    /**
384
     * @return bool|int
385
     */
386
    public function updateMulti($guids, $category, $grabs, $videoId, $episodeId, $anidbId, $imdbId)
387
    {
388
        if (! \is_array($guids) || \count($guids) < 1) {
389
            return false;
390
        }
391
392
        $update = [
393
            'categories_id' => $category === -1 ? 'categories_id' : $category,
394
            'grabs' => $grabs,
395
            'videos_id' => $videoId,
396
            'tv_episodes_id' => $episodeId,
397
            'anidbid' => $anidbId,
398
            'imdbid' => $imdbId,
399
        ];
400
401
        return self::query()->whereIn('guid', $guids)->update($update);
402
    }
403
404
    /**
405
     * Creates part of a query for some functions.
406
     */
407
    public function uSQL(Collection|array $userQuery, string $type): string
408
    {
409
        $sql = '(1=2 ';
410
        foreach ($userQuery as $query) {
411
            $sql .= sprintf('OR (r.%s = %d', $type, $query->$type);
412
            if (! empty($query->categories)) {
413
                $catsArr = explode('|', $query->categories);
414
                if (\count($catsArr) > 1) {
415
                    $sql .= sprintf(' AND r.categories_id IN (%s)', implode(',', $catsArr));
416
                } else {
417
                    $sql .= sprintf(' AND r.categories_id = %d', $catsArr[0]);
418
                }
419
            }
420
            $sql .= ') ';
421
        }
422
        $sql .= ') ';
423
424
        return $sql;
425
    }
426
427
    /**
428
     * Function for searching on the site (by subject, searchname or advanced).
429
     *
430
     *
431
     * @return array|Collection|mixed
432
     */
433
    public function search(array $searchArr, $groupName, $sizeFrom, $sizeTo, $daysNew, $daysOld, int $offset = 0, int $limit = 1000, array|string $orderBy = '', int $maxAge = -1, array $excludedCats = [], string $type = 'basic', array $cat = [-1], int $minSize = 0): mixed
434
    {
435
        $sizeRange = [
436
            1 => 1,
437
            2 => 2.5,
438
            3 => 5,
439
            4 => 10,
440
            5 => 20,
441
            6 => 30,
442
            7 => 40,
443
            8 => 80,
444
            9 => 160,
445
            10 => 320,
446
            11 => 640,
447
        ];
448
        if ($orderBy === '') {
449
            $orderBy = [];
450
            $orderBy[0] = 'postdate ';
451
            $orderBy[1] = 'desc ';
452
        } else {
453
            $orderBy = $this->getBrowseOrder($orderBy);
454
        }
455
456
        $searchFields = Arr::where($searchArr, static function ($value) {
457
            return $value !== -1;
458
        });
459
460
        $phrases = array_values($searchFields);
461
462
        if (config('nntmux.elasticsearch_enabled') === true) {
463
            $searchResult = $this->elasticSearch->indexSearch($phrases, $limit);
464
        } else {
465
            $searchResult = $this->manticoreSearch->searchIndexes('releases_rt', '', [], $searchFields);
466
            if (! empty($searchResult)) {
467
                $searchResult = Arr::wrap(Arr::get($searchResult, 'id'));
468
            }
469
        }
470
471
        if (empty($searchResult)) {
472
            return collect();
473
        }
474
475
        $catQuery = '';
476
        if ($type === 'basic') {
477
            $catQuery = Category::getCategorySearch($cat);
478
        } elseif ($type === 'advanced' && (int) $cat[0] !== -1) {
479
            $catQuery = sprintf('AND r.categories_id = %d', $cat[0]);
480
        }
481
        $whereSql = sprintf(
482
            'WHERE r.passwordstatus %s AND r.nzbstatus = %d %s %s %s %s %s %s %s %s %s %s',
483
            $this->showPasswords(),
484
            NZB::NZB_ADDED,
485
            ($maxAge > 0 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $maxAge) : ''),
486
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', UsenetGroup::getIDByName($groupName)) : ''),
0 ignored issues
show
Bug introduced by
It seems like App\Models\UsenetGroup::getIDByName($groupName) can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

486
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', /** @scrutinizer ignore-type */ UsenetGroup::getIDByName($groupName)) : ''),
Loading history...
487
            (array_key_exists($sizeFrom, $sizeRange) ? ' AND r.size > '.(104857600 * (int) $sizeRange[$sizeFrom]).' ' : ''),
488
            (array_key_exists($sizeTo, $sizeRange) ? ' AND r.size < '.(104857600 * (int) $sizeRange[$sizeTo]).' ' : ''),
489
            $catQuery,
0 ignored issues
show
Bug introduced by
It seems like $catQuery can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

489
            /** @scrutinizer ignore-type */ $catQuery,
Loading history...
490
            ((int) $daysNew !== -1 ? sprintf(' AND r.postdate < (NOW() - INTERVAL %d DAY) ', $daysNew) : ''),
491
            ((int) $daysOld !== -1 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $daysOld) : ''),
492
            (\count($excludedCats) > 0 ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
493
            ('AND r.id IN ('.implode(',', $searchResult).')'),
494
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '')
495
        );
496
        $baseSql = sprintf(
497
            "SELECT r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus,  cp.title AS parent_category, c.title AS sub_category,
498
				CONCAT(cp.title, ' > ', c.title) AS category_name,
499
				df.failed AS failed,
500
				g.name AS group_name,
501
				rn.releases_id AS nfoid,
502
				re.releases_id AS reid,
503
				cp.id AS categoryparentid,
504
				v.tvdb, v.trakt, v.tvrage, v.tvmaze, v.imdb, v.tmdb,
505
				tve.firstaired
506
			FROM releases r
507
			LEFT OUTER JOIN video_data re ON re.releases_id = r.id
508
			LEFT OUTER JOIN videos v ON r.videos_id = v.id
509
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
510
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
511
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
512
			LEFT JOIN categories c ON c.id = r.categories_id
513
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
514
			LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
515
			%s",
516
            $whereSql
517
        );
518
        $sql = sprintf(
519
            'SELECT * FROM (
520
				%s
521
			) r
522
			ORDER BY r.%s %s
523
			LIMIT %d OFFSET %d',
524
            $baseSql,
525
            $orderBy[0],
526
            $orderBy[1],
527
            $limit,
528
            $offset
529
        );
530
        $releases = Cache::get(md5($sql));
531
        if ($releases !== null) {
532
            return $releases;
533
        }
534
        $releases = self::fromQuery($sql);
535
        if ($releases->isNotEmpty()) {
536
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
537
        }
538
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
539
        Cache::put(md5($sql), $releases, $expiresAt);
540
541
        return $releases;
542
    }
543
544
    /**
545
     * Search function for API.
546
     *
547
     *
548
     * @return Collection|mixed
549
     */
550
    public function apiSearch($searchName, $groupName, int $offset = 0, int $limit = 1000, int $maxAge = -1, array $excludedCats = [], array $cat = [-1], int $minSize = 0): mixed
551
    {
552
        if ($searchName !== -1) {
553
            if (config('nntmux.elasticsearch_enabled') === true) {
554
                $searchResult = $this->elasticSearch->indexSearchApi($searchName, $limit);
555
            } else {
556
                $searchResult = $this->manticoreSearch->searchIndexes('releases_rt', $searchName, ['searchname']);
557
                if (! empty($searchResult)) {
558
                    $searchResult = Arr::wrap(Arr::get($searchResult, 'id'));
559
                }
560
            }
561
        }
562
563
        $catQuery = Category::getCategorySearch($cat);
564
565
        $whereSql = sprintf(
566
            'WHERE r.passwordstatus %s AND r.nzbstatus = %d %s %s %s %s %s %s',
567
            $this->showPasswords(),
568
            NZB::NZB_ADDED,
569
            ($maxAge > 0 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $maxAge) : ''),
570
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', UsenetGroup::getIDByName($groupName)) : ''),
0 ignored issues
show
Bug introduced by
It seems like App\Models\UsenetGroup::getIDByName($groupName) can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

570
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', /** @scrutinizer ignore-type */ UsenetGroup::getIDByName($groupName)) : ''),
Loading history...
571
            $catQuery,
0 ignored issues
show
Bug introduced by
It seems like $catQuery can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

571
            /** @scrutinizer ignore-type */ $catQuery,
Loading history...
572
            (\count($excludedCats) > 0 ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
573
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
574
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '')
575
        );
576
        $baseSql = sprintf(
577
            "SELECT r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus, m.imdbid, m.tmdbid, m.traktid, cp.title AS parent_category, c.title AS sub_category,
578
				CONCAT(cp.title, ' > ', c.title) AS category_name,
579
				g.name AS group_name,
580
				cp.id AS categoryparentid,
581
				v.tvdb, v.trakt, v.tvrage, v.tvmaze, v.imdb, v.tmdb,
582
				tve.firstaired, tve.title, tve.series, tve.episode
583
			FROM releases r
584
			LEFT OUTER JOIN videos v ON r.videos_id = v.id
585
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
586
			LEFT JOIN movieinfo m ON m.id = r.movieinfo_id
587
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
588
			LEFT JOIN categories c ON c.id = r.categories_id
589
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
590
			%s",
591
            $whereSql
592
        );
593
        $sql = sprintf(
594
            'SELECT * FROM (
595
				%s
596
			) r
597
			ORDER BY r.postdate DESC
598
			LIMIT %d OFFSET %d',
599
            $baseSql,
600
            $limit,
601
            $offset
602
        );
603
        $releases = Cache::get(md5($sql));
604
        if ($releases !== null) {
605
            return $releases;
606
        }
607
        if ($searchName !== -1 && ! empty($searchResult)) {
608
            $releases = self::fromQuery($sql);
609
        } elseif ($searchName !== -1 && empty($searchResult)) {
610
            $releases = collect();
611
        } elseif ($searchName === -1) {
612
            $releases = self::fromQuery($sql);
613
        } else {
614
            $releases = collect();
615
        }
616
        if ($releases->isNotEmpty()) {
617
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
618
        }
619
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
620
        Cache::put(md5($sql), $releases, $expiresAt);
621
622
        return $releases;
623
    }
624
625
    /**
626
     * Search for TV shows via API.
627
     *
628
     * @return array|\Illuminate\Cache\|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection|mixed
629
     */
630
    public function tvSearch(array $siteIdArr = [], string $series = '', string $episode = '', string $airDate = '', int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed
631
    {
632
        $siteSQL = [];
633
        $showSql = '';
634
        foreach ($siteIdArr as $column => $Id) {
635
            if ($Id > 0) {
636
                $siteSQL[] = sprintf('v.%s = %d', $column, $Id);
637
            }
638
        }
639
640
        if (\count($siteSQL) > 0) {
641
            // If we have show info, find the Episode ID/Video ID first to avoid table scans
642
            $showQry = sprintf(
643
                "
644
				SELECT
645
					v.id AS video,
646
					GROUP_CONCAT(tve.id SEPARATOR ',') AS episodes
647
				FROM videos v
648
				LEFT JOIN tv_episodes tve ON v.id = tve.videos_id
649
				WHERE (%s) %s %s %s
650
				GROUP BY v.id
651
				LIMIT 1",
652
                implode(' OR ', $siteSQL),
653
                ($series !== '' ? sprintf('AND tve.series = %d', (int) preg_replace('/^s0*/i', '', $series)) : ''),
654
                ($episode !== '' ? sprintf('AND tve.episode = %d', (int) preg_replace('/^e0*/i', '', $episode)) : ''),
655
                ($airDate !== '' ? sprintf('AND DATE(tve.firstaired) = %s', escapeString($airDate)) : '')
656
            );
657
658
            $show = self::fromQuery($showQry);
659
660
            if ($show->isNotEmpty()) {
661
                if ((! empty($episode) && ! empty($series)) && $show[0]->episodes !== '') {
662
                    $showSql .= ' AND r.tv_episodes_id IN ('.$show[0]->episodes.') AND tve.series = '.$series;
663
                } elseif (! empty($episode) && $show[0]->episodes !== '') {
664
                    $showSql = sprintf('AND r.tv_episodes_id IN (%s)', $show[0]->episodes);
665
                } elseif (! empty($series) && empty($episode)) {
666
                    // If $series is set but episode is not, return Season Packs and Episodes
667
                    $showSql .= ' AND r.tv_episodes_id IN ('.$show[0]->episodes.') AND tve.series = '.$series;
668
                }
669
                if ($show[0]->video > 0) {
670
                    $showSql .= ' AND r.videos_id = '.$show[0]->video;
671
                }
672
            } else {
673
                // If we were passed Site ID Info and no match was found, do not run the query
674
                return [];
675
            }
676
        }
677
678
        // If $name is set it is a fallback search, add available SxxExx/airdate info to the query
679
        if (! empty($name) && $showSql === '') {
680
            if (! empty($series) && (int) $series < 1900) {
681
                $name .= sprintf(' S%s', str_pad($series, 2, '0', STR_PAD_LEFT));
682
                if (! empty($episode) && ! str_contains($episode, '/')) {
683
                    $name .= sprintf('E%s', str_pad($episode, 2, '0', STR_PAD_LEFT));
684
                }
685
                // If season is not empty but episode is, add a wildcard to the search
686
                if (empty($episode)) {
687
                    $name .= '*';
688
                }
689
            } elseif (! empty($airDate)) {
690
                $name .= sprintf(' %s', str_replace(['/', '-', '.', '_'], ' ', $airDate));
691
            }
692
        }
693
        if (! empty($name)) {
694
            if (config('nntmux.elasticsearch_enabled') === true) {
695
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
696
            } else {
697
                $searchResult = $this->manticoreSearch->searchIndexes('releases_rt', $name, ['searchname']);
698
                if (! empty($searchResult)) {
699
                    $searchResult = Arr::wrap(Arr::get($searchResult, 'id'));
700
                }
701
            }
702
703
            if (empty($searchResult)) {
704
                return collect();
705
            }
706
        }
707
        $whereSql = sprintf(
708
            'WHERE r.nzbstatus = %d
709
			AND r.passwordstatus %s
710
			%s %s %s %s %s %s',
711
            NZB::NZB_ADDED,
712
            $this->showPasswords(),
713
            $showSql,
714
            (! empty($name) && ! empty($searchResult)) ? 'AND r.id IN ('.implode(',', $searchResult).')' : '',
715
            Category::getCategorySearch($cat, 'tv'),
0 ignored issues
show
Bug introduced by
It seems like App\Models\Category::get...egorySearch($cat, 'tv') can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

715
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat, 'tv'),
Loading history...
716
            $maxAge > 0 ? sprintf('AND r.postdate > NOW() - INTERVAL %d DAY', $maxAge) : '',
717
            $minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '',
718
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : ''
719
        );
720
        $baseSql = sprintf(
721
            "SELECT r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus,
722
				v.title, v.countries_id, v.started, v.tvdb, v.trakt,
723
					v.imdb, v.tmdb, v.tvmaze, v.tvrage, v.source,
724
				tvi.summary, tvi.publisher, tvi.image,
725
				tve.series, tve.episode, tve.se_complete, tve.title, tve.firstaired, tve.summary, cp.title AS parent_category, c.title AS sub_category,
726
				CONCAT(cp.title, ' > ', c.title) AS category_name,
727
				g.name AS group_name,
728
				rn.releases_id AS nfoid,
729
				re.releases_id AS reid
730
			FROM releases r
731
			LEFT OUTER JOIN videos v ON r.videos_id = v.id AND v.type = 0
732
			LEFT OUTER JOIN tv_info tvi ON v.id = tvi.videos_id
733
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
734
			LEFT JOIN categories c ON c.id = r.categories_id
735
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
736
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
737
			LEFT OUTER JOIN video_data re ON re.releases_id = r.id
738
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
739
			%s",
740
            $whereSql
741
        );
742
        $sql = sprintf(
743
            '%s
744
			ORDER BY postdate DESC
745
			LIMIT %d OFFSET %d',
746
            $baseSql,
747
            $limit,
748
            $offset
749
        );
750
        $releases = Cache::get(md5($sql));
751
        if ($releases !== null) {
752
            return $releases;
753
        }
754
        $releases = ((! empty($name) && ! empty($searchResult)) || empty($name)) ? self::fromQuery($sql) : [];
755
        if (count($releases) !== 0 && $releases->isNotEmpty()) {
0 ignored issues
show
Bug introduced by
It seems like $releases can also be of type Illuminate\Database\Eloq...gHasThroughRelationship; however, parameter $value of count() does only seem to accept Countable|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

755
        if (count(/** @scrutinizer ignore-type */ $releases) !== 0 && $releases->isNotEmpty()) {
Loading history...
756
            $releases[0]->_totalrows = $this->getPagerCount(
757
                preg_replace('#LEFT(\s+OUTER)?\s+JOIN\s+(?!tv_episodes)\s+.*ON.*=.*\n#i', ' ', $baseSql)
758
            );
759
        }
760
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
761
        Cache::put(md5($sql), $releases, $expiresAt);
762
763
        return $releases;
764
    }
765
766
    /**
767
     * Search TV Shows via APIv2.
768
     *
769
     *
770
     * @return Collection|mixed
771
     */
772
    public function apiTvSearch(array $siteIdArr = [], string $series = '', string $episode = '', string $airDate = '', int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed
773
    {
774
        $siteSQL = [];
775
        $showSql = '';
776
        foreach ($siteIdArr as $column => $Id) {
777
            if ($Id > 0) {
778
                $siteSQL[] = sprintf('v.%s = %d', $column, $Id);
779
            }
780
        }
781
782
        if (\count($siteSQL) > 0) {
783
            // If we have show info, find the Episode ID/Video ID first to avoid table scans
784
            $showQry = sprintf(
785
                "
786
				SELECT
787
					v.id AS video,
788
					GROUP_CONCAT(tve.id SEPARATOR ',') AS episodes
789
				FROM videos v
790
				LEFT JOIN tv_episodes tve ON v.id = tve.videos_id
791
				WHERE (%s) %s %s %s
792
				GROUP BY v.id
793
				LIMIT 1",
794
                implode(' OR ', $siteSQL),
795
                ($series !== '' ? sprintf('AND tve.series = %d', (int) preg_replace('/^s0*/i', '', $series)) : ''),
796
                ($episode !== '' ? sprintf('AND tve.episode = %d', (int) preg_replace('/^e0*/i', '', $episode)) : ''),
797
                ($airDate !== '' ? sprintf('AND DATE(tve.firstaired) = %s', escapeString($airDate)) : '')
798
            );
799
800
            $show = self::fromQuery($showQry);
801
            if ($show->isNotEmpty()) {
802
                if ((! empty($episode) && ! empty($series)) && $show[0]->episodes !== '') {
803
                    $showSql .= ' AND r.tv_episodes_id IN ('.$show[0]->episodes.') AND tve.series = '.$series;
804
                } elseif (! empty($episode) && $show[0]->episodes !== '') {
805
                    $showSql = sprintf('AND r.tv_episodes_id IN (%s)', $show[0]->episodes);
806
                } elseif (! empty($series) && empty($episode)) {
807
                    // If $series is set but episode is not, return Season Packs and Episodes
808
                    $showSql .= ' AND r.tv_episodes_id IN ('.$show[0]->episodes.') AND tve.series = '.$series;
809
                }
810
                if ($show[0]->video > 0) {
811
                    $showSql .= ' AND r.videos_id = '.$show[0]->video;
812
                }
813
            } else {
814
                // If we were passed Site ID Info and no match was found, do not run the query
815
                return [];
816
            }
817
        }
818
        // If $name is set it is a fallback search, add available SxxExx/airdate info to the query
819
        if (! empty($name) && $showSql === '') {
820
            if (! empty($series) && (int) $series < 1900) {
821
                $name .= sprintf(' S%s', str_pad($series, 2, '0', STR_PAD_LEFT));
822
                if (! empty($episode) && ! str_contains($episode, '/')) {
823
                    $name .= sprintf('E%s', str_pad($episode, 2, '0', STR_PAD_LEFT));
824
                }
825
                // If season is not empty but episode is, add a wildcard to the search
826
                if (empty($episode)) {
827
                    $name .= '*';
828
                }
829
            } elseif (! empty($airDate)) {
830
                $name .= sprintf(' %s', str_replace(['/', '-', '.', '_'], ' ', $airDate));
831
            }
832
        }
833
        if (! empty($name)) {
834
            if (config('nntmux.elasticsearch_enabled') === true) {
835
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
836
            } else {
837
                $searchResult = $this->manticoreSearch->searchIndexes('releases_rt', $name, ['searchname']);
838
                if (! empty($searchResult)) {
839
                    $searchResult = Arr::wrap(Arr::get($searchResult, 'id'));
840
                }
841
            }
842
843
            if (empty($searchResult)) {
844
                return collect();
845
            }
846
        }
847
        $whereSql = sprintf(
848
            'WHERE r.nzbstatus = %d
849
			AND r.passwordstatus %s
850
			%s %s %s %s %s %s',
851
            NZB::NZB_ADDED,
852
            $this->showPasswords(),
853
            $showSql,
854
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
855
            Category::getCategorySearch($cat, 'tv'),
0 ignored issues
show
Bug introduced by
It seems like App\Models\Category::get...egorySearch($cat, 'tv') can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

855
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat, 'tv'),
Loading history...
856
            ($maxAge > 0 ? sprintf('AND r.postdate > NOW() - INTERVAL %d DAY', $maxAge) : ''),
857
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''),
858
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : ''
859
        );
860
        $baseSql = sprintf(
861
            "SELECT r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.tv_episodes_id, r.haspreview, r.jpgstatus,
862
				v.title, v.type, v.tvdb, v.trakt,v.imdb, v.tmdb, v.tvmaze, v.tvrage,
863
				tve.series, tve.episode, tve.se_complete, tve.title, tve.firstaired, cp.title AS parent_category, c.title AS sub_category,
864
				CONCAT(cp.title, ' > ', c.title) AS category_name,
865
				g.name AS group_name
866
			FROM releases r
867
			LEFT OUTER JOIN videos v ON r.videos_id = v.id AND v.type = 0
868
			LEFT OUTER JOIN tv_info tvi ON v.id = tvi.videos_id
869
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
870
			LEFT JOIN categories c ON c.id = r.categories_id
871
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
872
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
873
			%s",
874
            $whereSql
875
        );
876
        $sql = sprintf(
877
            '%s
878
			ORDER BY postdate DESC
879
			LIMIT %d OFFSET %d',
880
            $baseSql,
881
            $limit,
882
            $offset
883
        );
884
        $releases = Cache::get(md5($sql));
885
        if ($releases !== null) {
886
            return $releases;
887
        }
888
        $releases = self::fromQuery($sql);
889
        if ($releases->isNotEmpty()) {
890
            $releases[0]->_totalrows = $this->getPagerCount(
891
                preg_replace('#LEFT(\s+OUTER)?\s+JOIN\s+(?!tv_episodes)\s+.*ON.*=.*\n#i', ' ', $baseSql)
892
            );
893
        }
894
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
895
        Cache::put(md5($sql), $releases, $expiresAt);
896
897
        return $releases;
898
    }
899
900
    /**
901
     * Search anime releases.
902
     *
903
     *
904
     * @return Collection|mixed
905
     */
906
    public function animeSearch($aniDbID, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, array $excludedCategories = []): mixed
907
    {
908
        if (! empty($name)) {
909
            if (config('nntmux.elasticsearch_enabled') === true) {
910
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
911
            } else {
912
                $searchResult = $this->manticoreSearch->searchIndexes('releases_rt', $name, ['searchname']);
913
                if (! empty($searchResult)) {
914
                    $searchResult = Arr::wrap(Arr::get($searchResult, 'id'));
915
                }
916
            }
917
918
            if (empty($searchResult)) {
919
                return collect();
920
            }
921
        }
922
923
        $whereSql = sprintf(
924
            'WHERE r.passwordstatus %s
925
			AND r.nzbstatus = %d
926
			%s %s %s %s %s',
927
            $this->showPasswords(),
928
            NZB::NZB_ADDED,
929
            ($aniDbID > -1 ? sprintf(' AND r.anidbid = %d ', $aniDbID) : ''),
930
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
931
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : '',
932
            Category::getCategorySearch($cat),
0 ignored issues
show
Bug introduced by
It seems like App\Models\Category::getCategorySearch($cat) can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

932
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat),
Loading history...
933
            ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '')
934
        );
935
        $baseSql = sprintf(
936
            "SELECT r.id, r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.haspreview, r.jpgstatus,  cp.title AS parent_category, c.title AS sub_category,
937
				CONCAT(cp.title, ' > ', c.title) AS category_name,
938
				g.name AS group_name,
939
				rn.releases_id AS nfoid
940
			FROM releases r
941
			LEFT JOIN categories c ON c.id = r.categories_id
942
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
943
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
944
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
945
			%s",
946
            $whereSql
947
        );
948
        $sql = sprintf(
949
            '%s
950
			ORDER BY postdate DESC
951
			LIMIT %d OFFSET %d',
952
            $baseSql,
953
            $limit,
954
            $offset
955
        );
956
        $releases = Cache::get(md5($sql));
957
        if ($releases !== null) {
958
            return $releases;
959
        }
960
        $releases = self::fromQuery($sql);
961
        if ($releases->isNotEmpty()) {
962
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
963
        }
964
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
965
        Cache::put(md5($sql), $releases, $expiresAt);
966
967
        return $releases;
968
    }
969
970
    /**
971
     * Movies search through API and site.
972
     *
973
     *
974
     * @return Collection|mixed
975
     */
976
    public function moviesSearch(int $imDbId = -1, int $tmDbId = -1, int $traktId = -1, int $offset = 0, int $limit = 100, string $name = '', array $cat = [-1], int $maxAge = -1, int $minSize = 0, array $excludedCategories = []): mixed
977
    {
978
        if (! empty($name)) {
979
            if (config('nntmux.elasticsearch_enabled') === true) {
980
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
981
            } else {
982
                $searchResult = $this->manticoreSearch->searchIndexes('releases_rt', $name, ['searchname']);
983
                if (! empty($searchResult)) {
984
                    $searchResult = Arr::wrap(Arr::get($searchResult, 'id'));
985
                }
986
            }
987
988
            if (empty($searchResult)) {
989
                return collect();
990
            }
991
        }
992
993
        $whereSql = sprintf(
994
            'WHERE r.categories_id BETWEEN '.Category::MOVIE_ROOT.' AND '.Category::MOVIE_OTHER.'
995
			AND r.nzbstatus = %d
996
			AND r.passwordstatus %s
997
			%s %s %s %s %s %s %s',
998
            NZB::NZB_ADDED,
999
            $this->showPasswords(),
1000
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
1001
            ($imDbId !== -1 && is_numeric($imDbId)) ? sprintf(' AND m.imdbid = \'%s\' ', $imDbId) : '',
1002
            ($tmDbId !== -1 && is_numeric($tmDbId)) ? sprintf(' AND m.tmdbid = %d ', $tmDbId) : '',
1003
            ($traktId !== -1 && is_numeric($traktId)) ? sprintf(' AND m.traktid = %d ', $traktId) : '',
1004
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : '',
1005
            Category::getCategorySearch($cat, 'movies'),
0 ignored issues
show
Bug introduced by
It seems like App\Models\Category::get...ySearch($cat, 'movies') can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, 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

1005
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat, 'movies'),
Loading history...
1006
            $maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '',
1007
            $minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''
1008
        );
1009
        $baseSql = sprintf(
1010
            "SELECT r.id, r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.imdbid, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus, m.imdbid, m.tmdbid, m.traktid, cp.title AS parent_category, c.title AS sub_category,
1011
				concat(cp.title, ' > ', c.title) AS category_name,
1012
				g.name AS group_name,
1013
				rn.releases_id AS nfoid
1014
			FROM releases r
1015
			LEFT JOIN movieinfo m ON m.id = r.movieinfo_id
1016
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
1017
			LEFT JOIN categories c ON c.id = r.categories_id
1018
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
1019
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
1020
			%s",
1021
            $whereSql
1022
        );
1023
        $sql = sprintf(
1024
            '%s
1025
			ORDER BY postdate DESC
1026
			LIMIT %d OFFSET %d',
1027
            $baseSql,
1028
            $limit,
1029
            $offset
1030
        );
1031
1032
        $releases = Cache::get(md5($sql));
1033
        if ($releases !== null) {
1034
            return $releases;
1035
        }
1036
        $releases = self::fromQuery($sql);
1037
        if ($releases->isNotEmpty()) {
1038
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
1039
        }
1040
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
1041
        Cache::put(md5($sql), $releases, $expiresAt);
1042
1043
        return $releases;
1044
    }
1045
1046
    public function searchSimilar($currentID, $name, array $excludedCats = []): bool|array
1047
    {
1048
        // Get the category for the parent of this release.
1049
        $ret = false;
1050
        $currRow = self::getCatByRelId($currentID);
1051
        if ($currRow !== null) {
1052
            $catRow = Category::find($currRow['categories_id']);
1053
            $parentCat = $catRow !== null ? $catRow['root_categories_id'] : null;
1054
1055
            if ($parentCat === null) {
1056
                return $ret;
1057
            }
1058
1059
            $results = $this->search(['searchname' => getSimilarName($name)], -1, '', '', -1, -1, 0, config('nntmux.items_per_page'), '', -1, $excludedCats, 'basic', [$parentCat]);
1060
            if (! $results) {
1061
                return $ret;
1062
            }
1063
1064
            $ret = [];
1065
            foreach ($results as $res) {
1066
                if ($res['id'] !== $currentID && $res['categoryparentid'] === $parentCat) {
1067
                    $ret[] = $res;
1068
                }
1069
            }
1070
        }
1071
1072
        return $ret;
1073
    }
1074
1075
    /**
1076
     * Get count of releases for pager.
1077
     *
1078
     * @param  string  $query  The query to get the count from.
1079
     */
1080
    private function getPagerCount(string $query): int
1081
    {
1082
        $queryBuilder = DB::table(DB::raw('('.preg_replace(
1083
            '/SELECT.+?FROM\s+releases/is',
1084
            'SELECT r.id FROM releases',
1085
            $query
1086
        ).' LIMIT '.(int) config('nntmux.max_pager_results').') as z'))
1087
            ->selectRaw('COUNT(z.id) as count');
1088
1089
        $sql = $queryBuilder->toSql();
1090
        $count = Cache::get(md5($sql));
1091
1092
        if ($count !== null) {
1093
            return $count;
1094
        }
1095
1096
        $result = $queryBuilder->first();
1097
        $count = $result->count ?? 0;
1098
1099
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_short'));
1100
        Cache::put(md5($sql), $count, $expiresAt);
1101
1102
        return $count;
1103
    }
1104
}
1105