Passed
Push — master ( 68504f...ba1af3 )
by Darko
03:47
created

Releases::getBrowseRange()   C

Complexity

Conditions 10
Paths 192

Size

Total Lines 62
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 5 Features 0
Metric Value
eloc 36
c 10
b 5
f 0
dl 0
loc 62
rs 6.9
cc 10
nc 192
nop 9

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
     * @return Collection|mixed
49
     */
50
    public function getBrowseRange($page, $cat, $start, $num, $orderBy, int $maxAge = -1, array $excludedCats = [], int|string $groupName = -1, int $minSize = 0): mixed
51
    {
52
        $page = max(1, $page);
53
        $start = max(0, $start);
54
55
        $orderBy = $this->getBrowseOrder($orderBy);
56
57
        $query = self::query()
58
            ->with(['group', 'category', 'category.parent', 'video', 'video.episode', 'videoData', 'nfo', 'failed'])
59
            ->where('nzbstatus', NZB::NZB_ADDED)
60
            ->where('passwordstatus', $this->showPasswords());
61
62
        if ($cat) {
63
            $categories = Category::getCategorySearch($cat, null, true);
64
            // If categories is empty, we don't want to return anything.
65
            if (! empty($categories)) {
66
                // if we have more than one category, we need to use whereIn
67
                if (\count($categories) > 1) {
0 ignored issues
show
Bug introduced by
It seems like $categories can also be of type string; 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

67
                if (\count(/** @scrutinizer ignore-type */ $categories) > 1) {
Loading history...
68
                    $query->whereIn('categories_id', $categories);
69
                } else {
70
                    $query->where('categories_id', $categories);
71
                }
72
            }
73
        }
74
75
        if ($maxAge > 0) {
76
            $query->where('postdate', '>', now()->subDays($maxAge));
77
        }
78
79
        if (! empty($excludedCats)) {
80
            $query->whereNotIn('categories_id', $excludedCats);
81
        }
82
83
        if ($groupName !== -1) {
84
            $query->whereHas('usenetGroup', function ($q) use ($groupName) {
85
                $q->where('name', $groupName);
86
            });
87
        }
88
89
        if ($minSize > 0) {
90
            $query->where('size', '>=', $minSize);
91
        }
92
93
        $query->orderBy($orderBy[0], $orderBy[1])
94
            ->skip($start)
95
            ->take($num);
96
97
        $releases = Cache::get(md5($query->toSql().$page));
0 ignored issues
show
Bug introduced by
Are you sure $query->toSql() of type Illuminate\Database\Eloquent\Builder|mixed|string can be used in concatenation? ( Ignorable by Annotation )

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

97
        $releases = Cache::get(md5(/** @scrutinizer ignore-type */ $query->toSql().$page));
Loading history...
98
        if ($releases !== null) {
99
            return $releases;
100
        }
101
102
        $sql = $query->get();
103
        if ($sql->isNotEmpty()) {
104
            $possibleRows = $this->getBrowseCount($cat, $maxAge, $excludedCats, $groupName);
105
            $sql[0]->_totalcount = $sql[0]->_totalrows = $possibleRows;
106
        }
107
108
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
109
        Cache::put(md5($query->toSql().$page), $sql, $expiresAt);
110
111
        return $sql;
112
    }
113
114
    /**
115
     * Used for pager on browse page.
116
     */
117
    public function getBrowseCount(array $cat, int $maxAge = -1, array $excludedCats = [], int|string $groupName = ''): int
118
    {
119
        return $this->getPagerCount(sprintf(
120
            'SELECT COUNT(r.id) AS count
121
				FROM releases r
122
				%s
123
				WHERE r.nzbstatus = %d
124
				AND r.passwordstatus %s
125
				%s
126
				%s %s %s ',
127
            ($groupName !== -1 ? 'LEFT JOIN usenet_groups g ON g.id = r.groups_id' : ''),
128
            NZB::NZB_ADDED,
129
            $this->showPasswords(),
130
            ($groupName !== -1 ? sprintf(' AND g.name = %s', escapeString($groupName)) : ''),
131
            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

131
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat),
Loading history...
132
            ($maxAge > 0 ? (' AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ') : ''),
133
            (\count($excludedCats) ? (' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')') : '')
134
        ));
135
    }
136
137
    public function showPasswords(): string
138
    {
139
        $show = (int) Settings::settingValue('showpasswordedrelease');
140
        $setting = $show ?? 0;
141
142
        return match ($setting) {
143
            1 => '<= '.self::PASSWD_RAR,
144
            default => '= '.self::PASSWD_NONE,
145
        };
146
    }
147
148
    /**
149
     * Use to order releases on site.
150
     */
151
    public function getBrowseOrder(array|string $orderBy): array
152
    {
153
        $orderArr = explode('_', ($orderBy === '' ? 'posted_desc' : $orderBy));
0 ignored issues
show
introduced by
The condition $orderBy === '' is always false.
Loading history...
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

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

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

490
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', /** @scrutinizer ignore-type */ UsenetGroup::getIDByName($groupName)) : ''),
Loading history...
491
            (array_key_exists($sizeFrom, $sizeRange) ? ' AND r.size > '.(104857600 * (int) $sizeRange[$sizeFrom]).' ' : ''),
492
            (array_key_exists($sizeTo, $sizeRange) ? ' AND r.size < '.(104857600 * (int) $sizeRange[$sizeTo]).' ' : ''),
493
            $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

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

574
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', /** @scrutinizer ignore-type */ UsenetGroup::getIDByName($groupName)) : ''),
Loading history...
575
            $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

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

719
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat, 'tv'),
Loading history...
720
            $maxAge > 0 ? sprintf('AND r.postdate > NOW() - INTERVAL %d DAY', $maxAge) : '',
721
            $minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '',
722
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : ''
723
        );
724
        $baseSql = sprintf(
725
            "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,
726
				v.title, v.countries_id, v.started, v.tvdb, v.trakt,
727
					v.imdb, v.tmdb, v.tvmaze, v.tvrage, v.source,
728
				tvi.summary, tvi.publisher, tvi.image,
729
				tve.series, tve.episode, tve.se_complete, tve.title, tve.firstaired, tve.summary, cp.title AS parent_category, c.title AS sub_category,
730
				CONCAT(cp.title, ' > ', c.title) AS category_name,
731
				g.name AS group_name,
732
				rn.releases_id AS nfoid,
733
				re.releases_id AS reid
734
			FROM releases r
735
			LEFT OUTER JOIN videos v ON r.videos_id = v.id AND v.type = 0
736
			LEFT OUTER JOIN tv_info tvi ON v.id = tvi.videos_id
737
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
738
			LEFT JOIN categories c ON c.id = r.categories_id
739
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
740
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
741
			LEFT OUTER JOIN video_data re ON re.releases_id = r.id
742
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
743
			%s",
744
            $whereSql
745
        );
746
        $sql = sprintf(
747
            '%s
748
			ORDER BY postdate DESC
749
			LIMIT %d OFFSET %d',
750
            $baseSql,
751
            $limit,
752
            $offset
753
        );
754
        $releases = Cache::get(md5($sql));
755
        if ($releases !== null) {
756
            return $releases;
757
        }
758
        $releases = ((! empty($name) && ! empty($searchResult)) || empty($name)) ? self::fromQuery($sql) : [];
759
        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

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

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

936
            /** @scrutinizer ignore-type */ Category::getCategorySearch($cat),
Loading history...
937
            ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '')
938
        );
939
        $baseSql = sprintf(
940
            "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,
941
				CONCAT(cp.title, ' > ', c.title) AS category_name,
942
				g.name AS group_name,
943
				rn.releases_id AS nfoid
944
			FROM releases r
945
			LEFT JOIN categories c ON c.id = r.categories_id
946
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
947
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
948
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
949
			%s",
950
            $whereSql
951
        );
952
        $sql = sprintf(
953
            '%s
954
			ORDER BY postdate DESC
955
			LIMIT %d OFFSET %d',
956
            $baseSql,
957
            $limit,
958
            $offset
959
        );
960
        $releases = Cache::get(md5($sql));
961
        if ($releases !== null) {
962
            return $releases;
963
        }
964
        $releases = self::fromQuery($sql);
965
        if ($releases->isNotEmpty()) {
966
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
967
        }
968
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
969
        Cache::put(md5($sql), $releases, $expiresAt);
970
971
        return $releases;
972
    }
973
974
    /**
975
     * Movies search through API and site.
976
     *
977
     *
978
     * @return Collection|mixed
979
     */
980
    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
981
    {
982
        if (! empty($name)) {
983
            if (config('nntmux.elasticsearch_enabled') === true) {
984
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
985
            } else {
986
                $searchResult = $this->manticoreSearch->searchIndexes('releases_rt', $name, ['searchname']);
987
                if (! empty($searchResult)) {
988
                    $searchResult = Arr::wrap(Arr::get($searchResult, 'id'));
989
                }
990
            }
991
992
            if (empty($searchResult)) {
993
                return collect();
994
            }
995
        }
996
997
        $whereSql = sprintf(
998
            'WHERE r.categories_id BETWEEN '.Category::MOVIE_ROOT.' AND '.Category::MOVIE_OTHER.'
999
			AND r.nzbstatus = %d
1000
			AND r.passwordstatus %s
1001
			%s %s %s %s %s %s %s',
1002
            NZB::NZB_ADDED,
1003
            $this->showPasswords(),
1004
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
1005
            ($imDbId !== -1 && is_numeric($imDbId)) ? sprintf(' AND m.imdbid = \'%s\' ', $imDbId) : '',
1006
            ($tmDbId !== -1 && is_numeric($tmDbId)) ? sprintf(' AND m.tmdbid = %d ', $tmDbId) : '',
1007
            ($traktId !== -1 && is_numeric($traktId)) ? sprintf(' AND m.traktid = %d ', $traktId) : '',
1008
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : '',
1009
            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

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