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

Releases::moviesSearch()   C

Complexity

Conditions 17
Paths 15

Size

Total Lines 68
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 14
Bugs 7 Features 0
Metric Value
eloc 50
c 14
b 7
f 0
dl 0
loc 68
rs 5.2166
cc 17
nc 15
nop 10

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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