Passed
Push — dev ( fca47e...7949da )
by Darko
08:11
created

Releases::tvSearch()   F

Complexity

Conditions 39
Paths 2466

Size

Total Lines 126
Code Lines 98

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 1560

Importance

Changes 19
Bugs 6 Features 0
Metric Value
eloc 98
c 19
b 6
f 0
dl 0
loc 126
ccs 0
cts 59
cp 0
rs 0
cc 39
nc 2466
nop 12
crap 1560

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\Common\Exceptions\Missing404Exception;
10
use Illuminate\Database\Eloquent\Collection;
11
use Illuminate\Support\Arr;
12
use Illuminate\Support\Facades\Cache;
13
use Illuminate\Support\Facades\DB;
14
use Illuminate\Support\Facades\File;
15
16
/**
17
 * Class Releases.
18
 */
19
class Releases extends Release
20
{
21
    // RAR/ZIP Passworded indicator.
22
    public const PASSWD_NONE = 0; // No password.
23
    public const PASSWD_RAR = 1; // Definitely passworded.
24
25
    /**
26
     * @var \Blacklight\SphinxSearch
27
     */
28
    public $sphinxSearch;
29
30
    /**
31
     * @var int
32
     */
33
    public $passwordStatus;
34
    /**
35
     * @var ElasticSearchSiteSearch
36
     */
37
    private $elasticSearch;
38
39
    /**
40
     * @var array Class instances.
41
     * @throws \Exception
42
     */
43
    public function __construct()
44
    {
45
        parent::__construct();
46
        $this->sphinxSearch = new SphinxSearch();
47
        $this->elasticSearch = new ElasticSearchSiteSearch();
48
    }
49
50
    /**
51
     * Used for Browse results.
52
     *
53
     *
54
     * @param       $page
55
     * @param       $cat
56
     * @param       $start
57
     * @param       $num
58
     * @param       $orderBy
59
     * @param int   $maxAge
60
     * @param array $excludedCats
61
     * @param array $tags
62
     * @param int   $groupName
63
     * @param int   $minSize
64
     *
65
     * @return Collection|mixed
66
     */
67
    public function getBrowseRange($page, $cat, $start, $num, $orderBy, $maxAge = -1, array $excludedCats = [], $groupName = -1, $minSize = 0, array $tags = [])
68
    {
69
        $orderBy = $this->getBrowseOrder($orderBy);
70
71
        $qry = sprintf(
72
            "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, r.group_name,
73
				CONCAT(cp.title, ' > ', c.title) AS category_name,
74
				CONCAT(cp.id, ',', c.id) AS category_ids,
75
				df.failed AS failed,
76
				rn.releases_id AS nfoid,
77
				re.releases_id AS reid,
78
				v.tvdb, v.trakt, v.tvrage, v.tvmaze, v.imdb, v.tmdb,
79
				tve.title, tve.firstaired
80
			FROM
81
			(
82
				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, g.name AS group_name
83
				FROM releases r
84
				LEFT JOIN usenet_groups g ON g.id = r.groups_id
85
				%s
86
				WHERE r.nzbstatus = %d
87
				AND r.passwordstatus %s
88
				%s %s %s %s %s %s
89
				ORDER BY %s %s %s
90
			) r
91
			LEFT JOIN categories c ON c.id = r.categories_id
92
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
93
			LEFT OUTER JOIN videos v ON r.videos_id = v.id
94
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
95
			LEFT OUTER JOIN video_data re ON re.releases_id = r.id
96
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
97
			LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
98
			GROUP BY r.id
99
			ORDER BY %10\$s %11\$s",
100
            ! empty($tags) ? ' LEFT JOIN tagging_tagged tt ON tt.taggable_id = r.id' : '',
101
            NZB::NZB_ADDED,
102
            $this->showPasswords(),
103
            ! empty($tags) ? " AND tt.tag_name IN ('".implode("','", $tags)."')" : '',
104
            Category::getCategorySearch($cat),
105
            ($maxAge > 0 ? (' AND postdate > NOW() - INTERVAL '.$maxAge.' DAY ') : ''),
106
            (\count($excludedCats) ? (' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')') : ''),
107
            ((int) $groupName !== -1 ? sprintf(' AND g.name = %s ', escapeString($groupName)) : ''),
108
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''),
109
            $orderBy[0],
110
            $orderBy[1],
111
            ($start === 0 ? ' LIMIT '.$num : ' LIMIT '.$num.' OFFSET '.$start)
112
        );
113
114
        $releases = Cache::get(md5($qry.$page));
115
        if ($releases !== null) {
116
            return $releases;
117
        }
118
        $sql = self::fromQuery($qry);
119
        if (\count($sql) > 0) {
120
            $possibleRows = $this->getBrowseCount($cat, $maxAge, $excludedCats, $groupName, $tags);
121
            $sql[0]->_totalcount = $sql[0]->_totalrows = $possibleRows;
122
        }
123
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
124
        Cache::put(md5($qry.$page), $sql, $expiresAt);
125
126
        return $sql;
127
    }
128
129
    /**
130
     * Used for pager on browse page.
131
     *
132
     * @param array      $cat
133
     * @param int        $maxAge
134
     * @param array      $excludedCats
135
     * @param string|int $groupName
136
     *
137
     * @param array      $tags
138
     *
139
     * @return int
140
     */
141
    public function getBrowseCount($cat, $maxAge = -1, array $excludedCats = [], $groupName = '', array $tags = []): int
142
    {
143
        return $this->getPagerCount(sprintf(
144
            'SELECT COUNT(r.id) AS count
145
				FROM releases r
146
				%s %s
147
				WHERE r.nzbstatus = %d
148
				AND r.passwordstatus %s
149
				%s
150
				%s %s %s %s ',
151
            ($groupName !== -1 ? 'LEFT JOIN usenet_groups g ON g.id = r.groups_id' : ''),
152
            ! empty($tags) ? ' LEFT JOIN tagging_tagged tt ON tt.taggable_id = r.id' : '',
153
            NZB::NZB_ADDED,
154
            $this->showPasswords(),
155
            ($groupName !== -1 ? sprintf(' AND g.name = %s', escapeString($groupName)) : ''),
156
            ! empty($tags) ? ' AND tt.tag_name IN ('.escapeString(implode(',', $tags)).')' : '',
157
            Category::getCategorySearch($cat),
158
            ($maxAge > 0 ? (' AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ') : ''),
159
            (\count($excludedCats) ? (' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')') : '')
160
        ));
161
    }
162
163
    /**
164
     * @return string
165
     */
166
    public function showPasswords()
167
    {
168
        $show = (int) Settings::settingValue('..showpasswordedrelease');
169
        $setting = $show ?? 0;
170
        switch ($setting) {
171
            case 1: // Shows everything.
172
                    return '<= '.self::PASSWD_RAR;
173
            case 0:
174
            default:// Hide releases with a password.
175
                return '= '.self::PASSWD_NONE;
176
        }
177
    }
178
179
    /**
180
     * Use to order releases on site.
181
     *
182
     * @param string|array $orderBy
183
     *
184
     * @return array
185
     */
186
    public function getBrowseOrder($orderBy): array
187
    {
188
        $orderArr = explode('_', ($orderBy === '' ? 'posted_desc' : $orderBy));
0 ignored issues
show
Bug introduced by
It seems like $orderBy === '' ? 'posted_desc' : $orderBy can also be of type array; however, parameter $string of explode() does only seem to accept 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

188
        $orderArr = explode('_', /** @scrutinizer ignore-type */ ($orderBy === '' ? 'posted_desc' : $orderBy));
Loading history...
189
        switch ($orderArr[0]) {
190
            case 'cat':
191
                $orderField = 'categories_id';
192
                break;
193
            case 'name':
194
                $orderField = 'searchname';
195
                break;
196
            case 'size':
197
                $orderField = 'size';
198
                break;
199
            case 'files':
200
                $orderField = 'totalpart';
201
                break;
202
            case 'stats':
203
                $orderField = 'grabs';
204
                break;
205
            case 'posted':
206
            default:
207
                $orderField = 'postdate';
208
                break;
209
        }
210
211
        return [$orderField, isset($orderArr[1]) && preg_match('/^(asc|desc)$/i', $orderArr[1]) ? $orderArr[1] : 'desc'];
212
    }
213
214
    /**
215
     * Return ordering types usable on site.
216
     *
217
     * @return string[]
218
     */
219
    public function getBrowseOrdering(): array
220
    {
221
        return [
222
            'name_asc',
223
            'name_desc',
224
            'cat_asc',
225
            'cat_desc',
226
            'posted_asc',
227
            'posted_desc',
228
            'size_asc',
229
            'size_desc',
230
            'files_asc',
231
            'files_desc',
232
            'stats_asc',
233
            'stats_desc',
234
        ];
235
    }
236
237
    /**
238
     * Get list of releases available for export.
239
     *
240
     *
241
     * @param string $postFrom
242
     * @param string $postTo
243
     * @param string $groupID
244
     *
245
     * @return Collection|\Illuminate\Support\Collection|static[]
246
     */
247
    public function getForExport($postFrom = '', $postTo = '', $groupID = '')
248
    {
249
        $query = self::query()
250
            ->where('r.nzbstatus', NZB::NZB_ADDED)
251
            ->select(['r.searchname', 'r.guid', 'g.name as gname', DB::raw("CONCAT(cp.title,'_',c.title) AS catName")])
252
            ->from('releases as r')
253
            ->leftJoin('categories as c', 'c.id', '=', 'r.categories_id')
254
            ->leftJoin('root_categories as cp', 'cp.id', '=', 'c.root_categories_id')
255
            ->leftJoin('usenet_groups as g', 'g.id', '=', 'r.groups_id');
256
257
        if ($groupID !== '') {
258
            $query->where('r.groups_id', $groupID);
259
        }
260
261
        if ($postFrom !== '') {
262
            $dateParts = explode('/', $postFrom);
263
            if (\count($dateParts) === 3) {
264
                $query->where('r.postdate', '>', $dateParts[2].'-'.$dateParts[1].'-'.$dateParts[0].'00:00:00');
265
            }
266
        }
267
268
        if ($postTo !== '') {
269
            $dateParts = explode('/', $postTo);
270
            if (\count($dateParts) === 3) {
271
                $query->where('r.postdate', '<', $dateParts[2].'-'.$dateParts[1].'-'.$dateParts[0].'23:59:59');
272
            }
273
        }
274
275
        return $query->get();
276
    }
277
278
    /**
279
     * Get date in this format : 01/01/2014 of the oldest release.
280
     *
281
     * @note Used for exporting NZBs.
282
     * @return mixed
283
     */
284
    public function getEarliestUsenetPostDate()
285
    {
286
        $row = self::query()->selectRaw("DATE_FORMAT(min(postdate), '%d/%m/%Y') AS postdate")->first();
287
288
        return $row === null ? '01/01/2014' : $row['postdate'];
289
    }
290
291
    /**
292
     * Get date in this format : 01/01/2014 of the newest release.
293
     *
294
     * @note Used for exporting NZBs.
295
     * @return mixed
296
     */
297
    public function getLatestUsenetPostDate()
298
    {
299
        $row = self::query()->selectRaw("DATE_FORMAT(max(postdate), '%d/%m/%Y') AS postdate")->first();
300
301
        return $row === null ? '01/01/2014' : $row['postdate'];
302
    }
303
304
    /**
305
     * Gets all groups for drop down selection on NZB-Export web page.
306
     *
307
     * @param bool $blnIncludeAll
308
     *
309
     * @note Used for exporting NZBs.
310
     * @return array
311
     */
312
    public function getReleasedGroupsForSelect($blnIncludeAll = true): array
313
    {
314
        $groups = self::query()
315
            ->selectRaw('DISTINCT g.id, g.name')
316
            ->leftJoin('usenet_groups as g', 'g.id', '=', 'releases.groups_id')
317
            ->get();
318
        $temp_array = [];
319
320
        if ($blnIncludeAll) {
321
            $temp_array[-1] = '--All Groups--';
322
        }
323
324
        foreach ($groups as $group) {
325
            $temp_array[$group['id']] = $group['name'];
326
        }
327
328
        return $temp_array;
329
    }
330
331
    /**
332
     * Get TV for My Shows page.
333
     *
334
     *
335
     * @param $userShows
336
     * @param $offset
337
     * @param $limit
338
     * @param $orderBy
339
     * @param int $maxAge
340
     * @param array $excludedCats
341
     * @return Collection|mixed
342
     */
343
    public function getShowsRange($userShows, $offset, $limit, $orderBy, $maxAge = -1, array $excludedCats = [])
344
    {
345
        $orderBy = $this->getBrowseOrder($orderBy);
346
        $sql = sprintf(
347
            "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,
348
					CONCAT(cp.title, '->', c.title) AS category_name
349
				FROM releases r
350
				LEFT JOIN categories c ON c.id = r.categories_id
351
				LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
352
				WHERE %s %s
353
				AND r.nzbstatus = %d
354
				AND r.categories_id BETWEEN %d AND %d
355
				AND r.passwordstatus %s
356
				%s
357
				GROUP BY r.id
358
				ORDER BY %s %s %s",
359
            $this->uSQL($userShows, 'videos_id'),
360
            (! empty($excludedCats) ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
361
            NZB::NZB_ADDED,
362
            Category::TV_ROOT,
363
            Category::TV_OTHER,
364
            $this->showPasswords(),
365
            ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : ''),
366
            $orderBy[0],
367
            $orderBy[1],
368
            ($offset === false ? '' : (' LIMIT '.$limit.' OFFSET '.$offset))
369
        );
370
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long'));
371
        $result = Cache::get(md5($sql));
372
        if ($result !== null) {
373
            return $result;
374
        }
375
        $result = self::fromQuery($sql);
376
        Cache::put(md5($sql), $result, $expiresAt);
377
378
        return $result;
379
    }
380
381
    /**
382
     * Get count for my shows page pagination.
383
     *
384
     * @param       $userShows
385
     * @param int   $maxAge
386
     * @param array $excludedCats
387
     *
388
     * @return int
389
     */
390
    public function getShowsCount($userShows, $maxAge = -1, array $excludedCats = []): int
391
    {
392
        return $this->getPagerCount(
393
            sprintf(
394
                'SELECT r.id
395
				FROM releases r
396
				WHERE %s %s
397
				AND r.nzbstatus = %d
398
				AND r.categories_id BETWEEN %d AND %d
399
				AND r.passwordstatus %s
400
				%s',
401
                $this->uSQL($userShows, 'videos_id'),
402
                (\count($excludedCats) ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
403
                NZB::NZB_ADDED,
404
                Category::TV_ROOT,
405
                Category::TV_OTHER,
406
                $this->showPasswords(),
407
                ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '')
408
            )
409
        );
410
    }
411
412
    /**
413
     * Delete multiple releases, or a single by ID.
414
     *
415
     * @param array|int|string $list   Array of GUID or ID of releases to delete.
416
     * @throws \Exception
417
     */
418
    public function deleteMultiple($list): void
419
    {
420
        $list = (array) $list;
421
422
        $nzb = new NZB();
423
        $releaseImage = new ReleaseImage();
424
425
        foreach ($list as $identifier) {
426
            $this->deleteSingle(['g' => $identifier, 'i' => false], $nzb, $releaseImage);
427
        }
428
    }
429
430
    /**
431
     * Deletes a single release by GUID, and all the corresponding files.
432
     *
433
     * @param array                    $identifiers ['g' => Release GUID(mandatory), 'id => ReleaseID(optional, pass
434
     *                                              false)]
435
     * @param \Blacklight\NZB          $nzb
436
     * @param \Blacklight\ReleaseImage $releaseImage
437
     *
438
     * @throws \Exception
439
     */
440
    public function deleteSingle($identifiers, NZB $nzb, ReleaseImage $releaseImage): void
441
    {
442
        // Delete NZB from disk.
443
        $nzbPath = $nzb->NZBPath($identifiers['g']);
444
        if (! empty($nzbPath)) {
445
            File::delete($nzbPath);
446
        }
447
448
        // Delete images.
449
        $releaseImage->delete($identifiers['g']);
450
451
        if (config('nntmux.elasticsearch_enabled') === true) {
452
            if ($identifiers['i'] === false) {
453
                $identifiers['i'] = Release::query()->where('guid', $identifiers['g'])->first(['id']);
454
                if ($identifiers['i'] !== null) {
455
                    $identifiers['i'] = $identifiers['i']['id'];
456
                }
457
            }
458
            if ($identifiers['i'] !== null) {
459
                $params = [
460
                    'index' => 'releases',
461
                    'id' => $identifiers['i'],
462
                ];
463
464
                try {
465
                    \Elasticsearch::delete($params);
466
                } catch (Missing404Exception $e) {
467
                    //we do nothing here just catch the error, we don't care if release is missing from ES, we are deleting it anyway
468
                }
469
            }
470
        } else {
471
            // Delete from sphinx.
472
            $this->sphinxSearch->deleteRelease($identifiers);
473
        }
474
475
        // Delete from DB.
476
        self::whereGuid($identifiers['g'])->delete();
477
    }
478
479
    /**
480
     * @param $guids
481
     * @param $category
482
     * @param $grabs
483
     * @param $videoId
484
     * @param $episodeId
485
     * @param $anidbId
486
     * @param $imdbId
487
     * @return bool|int
488
     */
489
    public function updateMulti($guids, $category, $grabs, $videoId, $episodeId, $anidbId, $imdbId)
490
    {
491
        if (! \is_array($guids) || \count($guids) < 1) {
492
            return false;
493
        }
494
495
        $update = [
496
            'categories_id'     => $category === -1 ? 'categories_id' : $category,
497
            'grabs'          => $grabs,
498
            'videos_id'      => $videoId,
499
            'tv_episodes_id' => $episodeId,
500
            'anidbid'        => $anidbId,
501
            'imdbid'         => $imdbId,
502
        ];
503
504
        return self::query()->whereIn('guid', $guids)->update($update);
505
    }
506
507
    /**
508
     * Creates part of a query for some functions.
509
     *
510
     * @param array|Collection  $userQuery
511
     * @param string $type
512
     *
513
     * @return string
514
     */
515
    public function uSQL($userQuery, $type): string
516
    {
517
        $sql = '(1=2 ';
518
        foreach ($userQuery as $query) {
519
            $sql .= sprintf('OR (r.%s = %d', $type, $query->$type);
520
            if (! empty($query->categories)) {
521
                $catsArr = explode('|', $query->categories);
522
                if (\count($catsArr) > 1) {
523
                    $sql .= sprintf(' AND r.categories_id IN (%s)', implode(',', $catsArr));
524
                } else {
525
                    $sql .= sprintf(' AND r.categories_id = %d', $catsArr[0]);
526
                }
527
            }
528
            $sql .= ') ';
529
        }
530
        $sql .= ') ';
531
532
        return $sql;
533
    }
534
535
    /**
536
     * Function for searching on the site (by subject, searchname or advanced).
537
     *
538
     *
539
     * @param array $searchArr
540
     * @param              $groupName
541
     * @param              $sizeFrom
542
     * @param              $sizeTo
543
     * @param              $daysNew
544
     * @param              $daysOld
545
     * @param int $offset
546
     * @param int $limit
547
     * @param string|array $orderBy
548
     * @param int $maxAge
549
     * @param array $excludedCats
550
     * @param string $type
551
     * @param array $cat
552
     * @param int $minSize
553
     * @param array $tags
554
     *
555
     * @return array|Collection|mixed
556
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
557
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
558
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
559
     */
560
    public function search($searchArr, $groupName, $sizeFrom, $sizeTo, $daysNew, $daysOld, $offset = 0, $limit = 1000, $orderBy = '', $maxAge = -1, array $excludedCats = [], $type = 'basic', array $cat = [-1], $minSize = 0, array $tags = [])
561
    {
562
        $sizeRange = [
563
            1 => 1,
564
            2 => 2.5,
565
            3 => 5,
566
            4 => 10,
567
            5 => 20,
568
            6 => 30,
569
            7 => 40,
570
            8 => 80,
571
            9 => 160,
572
            10 => 320,
573
            11 => 640,
574
        ];
575
        if ($orderBy === '') {
576
            $orderBy = [];
577
            $orderBy[0] = 'postdate ';
578
            $orderBy[1] = 'desc ';
579
        } else {
580
            $orderBy = $this->getBrowseOrder($orderBy);
581
        }
582
583
        $searchFields = Arr::where($searchArr, function ($value) {
584
            return $value !== -1;
585
        });
586
587
        $phrases = [];
588
        foreach ($searchFields as $key => $value) {
589
            $phrases[] = $value;
590
        }
591
592
        if (config('nntmux.elasticsearch_enabled') === true) {
593
            $searchResult = $this->elasticSearch->indexSearch($phrases, $limit);
594
        } else {
595
            $results = $this->sphinxSearch->searchIndexes('releases_rt', '', [], $searchFields);
596
597
            $searchResult = Arr::pluck($results, 'id');
598
        }
599
600
        if (empty($searchResult)) {
601
            return collect();
602
        }
603
604
        $catQuery = '';
605
        if ($type === 'basic') {
606
            $catQuery = Category::getCategorySearch($cat);
607
        } elseif ($type === 'advanced' && (int) $cat[0] !== -1) {
608
            $catQuery = sprintf('AND r.categories_id = %d', $cat[0]);
609
        }
610
        $whereSql = sprintf(
611
            'WHERE r.passwordstatus %s AND r.nzbstatus = %d %s %s %s %s %s %s %s %s %s %s %s',
612
            $this->showPasswords(),
613
            NZB::NZB_ADDED,
614
            ! empty($tags) ? " AND tt.tag_name IN ('".implode("','", $tags)."')" : '',
615
            ($maxAge > 0 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $maxAge) : ''),
616
            ((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 $args of sprintf() does only seem to accept 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

616
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', /** @scrutinizer ignore-type */ UsenetGroup::getIDByName($groupName)) : ''),
Loading history...
617
            (array_key_exists($sizeFrom, $sizeRange) ? ' AND r.size > '.(104857600 * (int) $sizeRange[$sizeFrom]).' ' : ''),
618
            (array_key_exists($sizeTo, $sizeRange) ? ' AND r.size < '.(104857600 * (int) $sizeRange[$sizeTo]).' ' : ''),
619
            $catQuery,
620
            ((int) $daysNew !== -1 ? sprintf(' AND r.postdate < (NOW() - INTERVAL %d DAY) ', $daysNew) : ''),
621
            ((int) $daysOld !== -1 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $daysOld) : ''),
622
            (\count($excludedCats) > 0 ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
623
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
624
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '')
625
        );
626
        $baseSql = sprintf(
627
            "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,
628
				CONCAT(cp.title, ' > ', c.title) AS category_name,
629
				df.failed AS failed,
630
				g.name AS group_name,
631
				rn.releases_id AS nfoid,
632
				re.releases_id AS reid,
633
				cp.id AS categoryparentid,
634
				v.tvdb, v.trakt, v.tvrage, v.tvmaze, v.imdb, v.tmdb,
635
				tve.firstaired
636
			FROM releases r
637
			LEFT OUTER JOIN video_data re ON re.releases_id = r.id
638
			LEFT OUTER JOIN videos v ON r.videos_id = v.id
639
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
640
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
641
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
642
			LEFT JOIN categories c ON c.id = r.categories_id
643
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
644
			LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
645
			%s %s",
646
            ! empty($tags) ? ' LEFT JOIN tagging_tagged tt ON tt.taggable_id = r.id' : '',
647
            $whereSql
648
        );
649
        $sql = sprintf(
650
            'SELECT * FROM (
651
				%s
652
			) r
653
			ORDER BY r.%s %s
654
			LIMIT %d OFFSET %d',
655
            $baseSql,
656
            $orderBy[0],
657
            $orderBy[1],
658
            $limit,
659
            $offset
660
        );
661
        $releases = Cache::get(md5($sql));
662
        if ($releases !== null) {
663
            return $releases;
664
        }
665
        $releases = ! empty($searchResult) ? self::fromQuery($sql) : collect();
666
        if ($releases->isNotEmpty()) {
667
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
668
        }
669
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
670
        Cache::put(md5($sql), $releases, $expiresAt);
671
672
        return $releases;
673
    }
674
675
    /**
676
     * Search function for API.
677
     *
678
     *
679
     * @param       $searchName
680
     * @param       $groupName
681
     * @param int $offset
682
     * @param int $limit
683
     * @param int $maxAge
684
     * @param array $excludedCats
685
     * @param array $cat
686
     * @param int $minSize
687
     * @param array $tags
688
     *
689
     * @return Collection|mixed
690
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
691
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
692
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
693
     */
694
    public function apiSearch($searchName, $groupName, $offset = 0, $limit = 1000, $maxAge = -1, array $excludedCats = [], array $cat = [-1], $minSize = 0, array $tags = [])
695
    {
696
        if ($searchName !== -1) {
697
            if (config('nntmux.elasticsearch_enabled') === true) {
698
                $searchResult = $this->elasticSearch->indexSearchApi($searchName, $limit);
699
            } else {
700
                $searchResult = Arr::pluck($this->sphinxSearch->searchIndexes('releases_rt', $searchName, ['searchname']), 'id');
701
            }
702
        }
703
704
        $catQuery = Category::getCategorySearch($cat);
705
706
        $whereSql = sprintf(
707
            'WHERE r.passwordstatus %s AND r.nzbstatus = %d %s %s %s %s %s %s %s',
708
            $this->showPasswords(),
709
            NZB::NZB_ADDED,
710
            ! empty($tags) ? " AND tt.tag_name IN ('".implode("','", $tags)."')" : '',
711
            ($maxAge > 0 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $maxAge) : ''),
712
            ((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 $args of sprintf() does only seem to accept 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

712
            ((int) $groupName !== -1 ? sprintf(' AND r.groups_id = %d ', /** @scrutinizer ignore-type */ UsenetGroup::getIDByName($groupName)) : ''),
Loading history...
713
            $catQuery,
714
            (\count($excludedCats) > 0 ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
715
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
716
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '')
717
        );
718
        $baseSql = sprintf(
719
            "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,
720
				CONCAT(cp.title, ' > ', c.title) AS category_name,
721
				g.name AS group_name,
722
				cp.id AS categoryparentid,
723
				v.tvdb, v.trakt, v.tvrage, v.tvmaze, v.imdb, v.tmdb,
724
				tve.firstaired, tve.title, tve.series, tve.episode
725
			FROM releases r
726
			LEFT OUTER JOIN videos v ON r.videos_id = v.id
727
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
728
			LEFT JOIN movieinfo m ON m.id = r.movieinfo_id
729
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
730
			LEFT JOIN categories c ON c.id = r.categories_id
731
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
732
			%s %s",
733
            ! empty($tags) ? ' LEFT JOIN tagging_tagged tt ON tt.taggable_id = r.id' : '',
734
            $whereSql
735
        );
736
        $sql = sprintf(
737
            'SELECT * FROM (
738
				%s
739
			) r
740
			ORDER BY r.postdate DESC
741
			LIMIT %d OFFSET %d',
742
            $baseSql,
743
            $limit,
744
            $offset
745
        );
746
        $releases = Cache::get(md5($sql));
747
        if ($releases !== null) {
748
            return $releases;
749
        }
750
        if ($searchName !== -1 && ! empty($searchResult)) {
751
            $releases = self::fromQuery($sql);
752
        } elseif ($searchName !== -1 && empty($searchResult)) {
753
            $releases = collect();
754
        } elseif ($searchName === -1) {
755
            $releases = self::fromQuery($sql);
756
        } else {
757
            $releases = collect();
758
        }
759
        if ($releases->isNotEmpty()) {
760
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
761
        }
762
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
763
        Cache::put(md5($sql), $releases, $expiresAt);
764
765
        return $releases;
766
    }
767
768
    /**
769
     * Search TV Shows via API.
770
     *
771
     *
772
     * @param array $siteIdArr
773
     * @param string $series
774
     * @param string $episode
775
     * @param string $airDate
776
     * @param int $offset
777
     * @param int $limit
778
     * @param string $name
779
     * @param array $cat
780
     * @param int $maxAge
781
     * @param int $minSize
782
     * @param array $excludedCategories
783
     * @param array $tags
784
     * @return Collection|mixed
785
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
786
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
787
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
788
     */
789
    public function tvSearch(array $siteIdArr = [], $series = '', $episode = '', $airDate = '', $offset = 0, $limit = 100, $name = '', array $cat = [-1], $maxAge = -1, $minSize = 0, array $excludedCategories = [], array $tags = [])
790
    {
791
        $siteSQL = [];
792
        $showSql = '';
793
        foreach ($siteIdArr as $column => $Id) {
794
            if ($Id > 0) {
795
                $siteSQL[] = sprintf('v.%s = %d', $column, $Id);
796
            }
797
        }
798
        if (\count($siteSQL) > 0) {
799
            // If we have show info, find the Episode ID/Video ID first to avoid table scans
800
            $showQry = sprintf(
801
                "
802
				SELECT
803
					v.id AS video,
804
					GROUP_CONCAT(tve.id SEPARATOR ',') AS episodes
805
				FROM videos v
806
				LEFT JOIN tv_episodes tve ON v.id = tve.videos_id
807
				WHERE (%s) %s %s %s
808
				GROUP BY v.id
809
				LIMIT 1",
810
                implode(' OR ', $siteSQL),
811
                ($series !== '' ? sprintf('AND tve.series = %d', (int) preg_replace('/^s0*/i', '', $series)) : ''),
812
                ($episode !== '' ? sprintf('AND tve.episode = %d', (int) preg_replace('/^e0*/i', '', $episode)) : ''),
813
                ($airDate !== '' ? sprintf('AND DATE(tve.firstaired) = %s', escapeString($airDate)) : '')
814
            );
815
            $show = self::fromQuery($showQry);
816
            if (! empty($show[0]) && $show->isNotEmpty()) {
817
                if ((! empty($series) || ! empty($episode) || ! empty($airDate)) && $show[0]->episodes !== '') {
818
                    $showSql = sprintf('AND r.tv_episodes_id IN (%s)', $show[0]->episodes);
819
                } elseif ((int) $show[0]->video > 0) {
820
                    $showSql = 'AND r.videos_id = '.$show[0]->video;
821
                    // If $series is set but episode is not, return Season Packs only
822
                    if (! empty($series) && empty($episode)) {
823
                        $showSql .= ' AND r.tv_episodes_id = 0';
824
                    }
825
                } else {
826
                    // If we were passed Episode Info and no match was found, do not run the query
827
                    return [];
828
                }
829
            } else {
830
                // If we were passed Site ID Info and no match was found, do not run the query
831
                return [];
832
            }
833
        }
834
        // If $name is set it is a fallback search, add available SxxExx/airdate info to the query
835
        if (! empty($name) && $showSql === '') {
836
            if (! empty($series) && (int) $series < 1900) {
837
                $name .= sprintf(' S%s', str_pad($series, 2, '0', STR_PAD_LEFT));
838
                if (! empty($episode) && strpos($episode, '/') === false) {
839
                    $name .= sprintf('E%s', str_pad($episode, 2, '0', STR_PAD_LEFT));
840
                }
841
            } elseif (! empty($airDate)) {
842
                $name .= sprintf(' %s', str_replace(['/', '-', '.', '_'], ' ', $airDate));
843
            }
844
        }
845
        if (! empty($name)) {
846
            if (config('nntmux.elasticsearch_enabled') === true) {
847
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
848
            } else {
849
                $searchResult = Arr::pluck($this->sphinxSearch->searchIndexes('releases_rt', $name, ['searchname']), 'id');
850
            }
851
852
            if (empty($searchResult)) {
853
                return collect();
854
            }
855
        }
856
        $whereSql = sprintf(
857
            'WHERE r.nzbstatus = %d
858
			AND r.passwordstatus %s
859
			%s %s %s %s %s %s %s',
860
            NZB::NZB_ADDED,
861
            $this->showPasswords(),
862
            ! empty($tags) ? " AND tt.tag_name IN ('".implode("','", $tags)."')" : '',
863
            $showSql,
864
            (! empty($name) && ! empty($searchResult)) ? 'AND r.id IN ('.implode(',', $searchResult).')' : '',
865
            Category::getCategorySearch($cat),
866
            $maxAge > 0 ? sprintf('AND r.postdate > NOW() - INTERVAL %d DAY', $maxAge) : '',
867
            $minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '',
868
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : ''
869
        );
870
        $baseSql = sprintf(
871
            "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,
872
				v.title, v.countries_id, v.started, v.tvdb, v.trakt,
873
					v.imdb, v.tmdb, v.tvmaze, v.tvrage, v.source,
874
				tvi.summary, tvi.publisher, tvi.image,
875
				tve.series, tve.episode, tve.se_complete, tve.title, tve.firstaired, tve.summary, cp.title AS parent_category, c.title AS sub_category,
876
				CONCAT(cp.title, ' > ', c.title) AS category_name,
877
				g.name AS group_name,
878
				rn.releases_id AS nfoid,
879
				re.releases_id AS reid
880
			FROM releases r
881
			LEFT OUTER JOIN videos v ON r.videos_id = v.id AND v.type = 0
882
			LEFT OUTER JOIN tv_info tvi ON v.id = tvi.videos_id
883
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
884
			LEFT JOIN categories c ON c.id = r.categories_id
885
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
886
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
887
			LEFT OUTER JOIN video_data re ON re.releases_id = r.id
888
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
889
			%s %s",
890
            ! empty($tags) ? ' LEFT JOIN tagging_tagged tt ON tt.taggable_id = r.id' : '',
891
            $whereSql
892
        );
893
        $sql = sprintf(
894
            '%s
895
			ORDER BY postdate DESC
896
			LIMIT %d OFFSET %d',
897
            $baseSql,
898
            $limit,
899
            $offset
900
        );
901
        $releases = Cache::get(md5($sql));
902
        if ($releases !== null) {
903
            return $releases;
904
        }
905
        $releases = ((! empty($name) && ! empty($searchResult)) || empty($name)) ? self::fromQuery($sql) : [];
906
        if (! empty($releases) && $releases->isNotEmpty()) {
907
            $releases[0]->_totalrows = $this->getPagerCount(
908
                preg_replace('#LEFT(\s+OUTER)?\s+JOIN\s+(?!tv_episodes)\s+.*ON.*=.*\n#i', ' ', $baseSql)
909
            );
910
        }
911
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
912
        Cache::put(md5($sql), $releases, $expiresAt);
913
914
        return $releases;
915
    }
916
917
    /**
918
     * Search TV Shows via APIv2.
919
     *
920
     *
921
     * @param array $siteIdArr
922
     * @param string $series
923
     * @param string $episode
924
     * @param string $airDate
925
     * @param int $offset
926
     * @param int $limit
927
     * @param string $name
928
     * @param array $cat
929
     * @param int $maxAge
930
     * @param int $minSize
931
     * @param array $excludedCategories
932
     * @param array $tags
933
     * @return Collection|mixed
934
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
935
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
936
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
937
     */
938
    public function apiTvSearch(array $siteIdArr = [], $series = '', $episode = '', $airDate = '', $offset = 0, $limit = 100, $name = '', array $cat = [-1], $maxAge = -1, $minSize = 0, array $excludedCategories = [], array $tags = [])
939
    {
940
        $siteSQL = [];
941
        $showSql = '';
942
        foreach ($siteIdArr as $column => $Id) {
943
            if ($Id > 0) {
944
                $siteSQL[] = sprintf('v.%s = %d', $column, $Id);
945
            }
946
        }
947
        if (\count($siteSQL) > 0) {
948
            // If we have show info, find the Episode ID/Video ID first to avoid table scans
949
            $showQry = sprintf(
950
                "
951
				SELECT
952
					v.id AS video,
953
					GROUP_CONCAT(tve.id SEPARATOR ',') AS episodes
954
				FROM videos v
955
				LEFT JOIN tv_episodes tve ON v.id = tve.videos_id
956
				WHERE (%s) %s %s %s
957
				GROUP BY v.id
958
				LIMIT 1",
959
                implode(' OR ', $siteSQL),
960
                ($series !== '' ? sprintf('AND tve.series = %d', (int) preg_replace('/^s0*/i', '', $series)) : ''),
961
                ($episode !== '' ? sprintf('AND tve.episode = %d', (int) preg_replace('/^e0*/i', '', $episode)) : ''),
962
                ($airDate !== '' ? sprintf('AND DATE(tve.firstaired) = %s', escapeString($airDate)) : '')
963
            );
964
            $show = self::fromQuery($showQry);
965
            if ($show->isNotEmpty()) {
966
                if ((! empty($series) || ! empty($episode) || ! empty($airDate)) && $show[0]->episodes != '') {
967
                    $showSql = sprintf('AND r.tv_episodes_id IN (%s)', $show[0]->episodes);
968
                } elseif ((int) $show[0]->video > 0) {
969
                    $showSql = 'AND r.videos_id = '.$show[0]->video;
970
                    // If $series is set but episode is not, return Season Packs only
971
                    if (! empty($series) && empty($episode)) {
972
                        $showSql .= ' AND r.tv_episodes_id = 0';
973
                    }
974
                } else {
975
                    // If we were passed Episode Info and no match was found, do not run the query
976
                    return [];
977
                }
978
            } else {
979
                // If we were passed Site ID Info and no match was found, do not run the query
980
                return [];
981
            }
982
        }
983
        // If $name is set it is a fallback search, add available SxxExx/airdate info to the query
984
        if (! empty($name) && $showSql === '') {
985
            if (! empty($series) && (int) $series < 1900) {
986
                $name .= sprintf(' S%s', str_pad($series, 2, '0', STR_PAD_LEFT));
987
                if (! empty($episode) && strpos($episode, '/') === false) {
988
                    $name .= sprintf('E%s', str_pad($episode, 2, '0', STR_PAD_LEFT));
989
                }
990
            } elseif (! empty($airDate)) {
991
                $name .= sprintf(' %s', str_replace(['/', '-', '.', '_'], ' ', $airDate));
992
            }
993
        }
994
        if (! empty($name)) {
995
            if (config('nntmux.elasticsearch_enabled') === true) {
996
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
997
            } else {
998
                $searchResult = Arr::pluck($this->sphinxSearch->searchIndexes('releases_rt', $name, ['searchname']), 'id');
999
            }
1000
1001
            if (empty($searchResult)) {
1002
                return collect();
1003
            }
1004
        }
1005
        $whereSql = sprintf(
1006
            'WHERE r.nzbstatus = %d
1007
			AND r.passwordstatus %s
1008
			%s %s %s %s %s %s %s',
1009
            NZB::NZB_ADDED,
1010
            $this->showPasswords(),
1011
            ! empty($tags) ? " AND tt.tag_name IN ('".implode("','", $tags)."')" : '',
1012
            $showSql,
1013
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
1014
            Category::getCategorySearch($cat),
1015
            ($maxAge > 0 ? sprintf('AND r.postdate > NOW() - INTERVAL %d DAY', $maxAge) : ''),
1016
            ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''),
1017
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : ''
1018
        );
1019
        $baseSql = sprintf(
1020
            "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,
1021
				v.title, v.type, v.tvdb, v.trakt,v.imdb, v.tmdb, v.tvmaze, v.tvrage,
1022
				tve.series, tve.episode, tve.se_complete, tve.title, tve.firstaired, cp.title AS parent_category, c.title AS sub_category,
1023
				CONCAT(cp.title, ' > ', c.title) AS category_name,
1024
				g.name AS group_name
1025
			FROM releases r
1026
			LEFT OUTER JOIN videos v ON r.videos_id = v.id AND v.type = 0
1027
			LEFT OUTER JOIN tv_info tvi ON v.id = tvi.videos_id
1028
			LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
1029
			LEFT JOIN categories c ON c.id = r.categories_id
1030
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
1031
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
1032
			%s %s",
1033
            ! empty($tags) ? ' LEFT JOIN tagging_tagged tt ON tt.taggable_id = r.id' : '',
1034
            $whereSql
1035
        );
1036
        $sql = sprintf(
1037
            '%s
1038
			ORDER BY postdate DESC
1039
			LIMIT %d OFFSET %d',
1040
            $baseSql,
1041
            $limit,
1042
            $offset
1043
        );
1044
        $releases = Cache::get(md5($sql));
1045
        if ($releases !== null) {
1046
            return $releases;
1047
        }
1048
        $releases = self::fromQuery($sql);
1049
        if ($releases->isNotEmpty()) {
1050
            $releases[0]->_totalrows = $this->getPagerCount(
1051
                preg_replace('#LEFT(\s+OUTER)?\s+JOIN\s+(?!tv_episodes)\s+.*ON.*=.*\n#i', ' ', $baseSql)
1052
            );
1053
        }
1054
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
1055
        Cache::put(md5($sql), $releases, $expiresAt);
1056
1057
        return $releases;
1058
    }
1059
1060
    /**
1061
     * Search anime releases.
1062
     *
1063
     *
1064
     * @param $aniDbID
1065
     * @param int $offset
1066
     * @param int $limit
1067
     * @param string $name
1068
     * @param array $cat
1069
     * @param int $maxAge
1070
     * @param array $excludedCategories
1071
     * @return Collection|mixed
1072
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
1073
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
1074
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
1075
     */
1076
    public function animeSearch($aniDbID, $offset = 0, $limit = 100, $name = '', array $cat = [-1], $maxAge = -1, array $excludedCategories = [])
1077
    {
1078
        if (! empty($name)) {
1079
            if (config('nntmux.elasticsearch_enabled') === true) {
1080
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
1081
            } else {
1082
                $searchResult = Arr::pluck($this->sphinxSearch->searchIndexes('releases_rt', $name, ['searchname']), 'id');
1083
            }
1084
1085
            if (empty($searchResult)) {
1086
                return collect();
1087
            }
1088
        }
1089
1090
        $whereSql = sprintf(
1091
            'WHERE r.passwordstatus %s
1092
			AND r.nzbstatus = %d
1093
			%s %s %s %s %s',
1094
            $this->showPasswords(),
1095
            NZB::NZB_ADDED,
1096
            ($aniDbID > -1 ? sprintf(' AND r.anidbid = %d ', $aniDbID) : ''),
1097
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
1098
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : '',
1099
            Category::getCategorySearch($cat),
1100
            ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '')
1101
        );
1102
        $baseSql = sprintf(
1103
            "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,
1104
				CONCAT(cp.title, ' > ', c.title) AS category_name,
1105
				g.name AS group_name,
1106
				rn.releases_id AS nfoid,
1107
				re.releases_id AS reid
1108
			FROM releases r
1109
			LEFT JOIN categories c ON c.id = r.categories_id
1110
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
1111
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
1112
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
1113
			LEFT OUTER JOIN releaseextrafull re ON re.releases_id = r.id
1114
			%s",
1115
            $whereSql
1116
        );
1117
        $sql = sprintf(
1118
            '%s
1119
			ORDER BY postdate DESC
1120
			LIMIT %d OFFSET %d',
1121
            $baseSql,
1122
            $limit,
1123
            $offset
1124
        );
1125
        $releases = Cache::get(md5($sql));
1126
        if ($releases !== null) {
1127
            return $releases;
1128
        }
1129
        $releases = self::fromQuery($sql);
1130
        if ($releases->isNotEmpty()) {
1131
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
1132
        }
1133
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
1134
        Cache::put(md5($sql), $releases, $expiresAt);
1135
1136
        return $releases;
1137
    }
1138
1139
    /**
1140
     * Movies search through API and site.
1141
     *
1142
     *
1143
     * @param int $imDbId
1144
     * @param int $tmDbId
1145
     * @param int $traktId
1146
     * @param int $offset
1147
     * @param int $limit
1148
     * @param string $name
1149
     * @param array $cat
1150
     * @param int $maxAge
1151
     * @param int $minSize
1152
     * @param array $excludedCategories
1153
     * @param array $tags
1154
     * @return Collection|mixed
1155
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
1156
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
1157
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
1158
     */
1159
    public function moviesSearch($imDbId = -1, $tmDbId = -1, $traktId = -1, $offset = 0, $limit = 100, $name = '', array $cat = [-1], $maxAge = -1, $minSize = 0, array $excludedCategories = [], array $tags = [])
1160
    {
1161
        if (! empty($name)) {
1162
            if (config('nntmux.elasticsearch_enabled') === true) {
1163
                $searchResult = $this->elasticSearch->indexSearchTMA($name, $limit);
1164
            } else {
1165
                $searchResult = Arr::pluck($this->sphinxSearch->searchIndexes('releases_rt', $name, ['searchname']), 'id');
1166
            }
1167
1168
            if (empty($searchResult)) {
1169
                return collect();
1170
            }
1171
        }
1172
1173
        $whereSql = sprintf(
1174
            'WHERE r.categories_id BETWEEN '.Category::MOVIE_ROOT.' AND '.Category::MOVIE_OTHER.'
1175
			AND r.nzbstatus = %d
1176
			AND r.passwordstatus %s
1177
			%s %s %s %s %s %s %s %s',
1178
            NZB::NZB_ADDED,
1179
            $this->showPasswords(),
1180
            (! empty($searchResult) ? 'AND r.id IN ('.implode(',', $searchResult).')' : ''),
1181
            ! empty($tags) ? " AND tt.tag_name IN ('".implode("','", $tags)."')" : '',
1182
            ($imDbId !== -1 && is_numeric($imDbId)) ? sprintf(' AND m.imdbid = %d ', $imDbId) : '',
1183
            ($tmDbId !== -1 && is_numeric($tmDbId)) ? sprintf(' AND m.tmdbid = %d ', $tmDbId) : '',
1184
            ($traktId !== -1 && is_numeric($traktId)) ? sprintf(' AND m.traktid = %d ', $traktId) : '',
1185
            ! empty($excludedCategories) ? sprintf('AND r.categories_id NOT IN('.implode(',', $excludedCategories).')') : '',
1186
            Category::getCategorySearch($cat),
1187
            $maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '',
1188
            $minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''
1189
        );
1190
        $baseSql = sprintf(
1191
            "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,
1192
				concat(cp.title, ' > ', c.title) AS category_name,
1193
				g.name AS group_name,
1194
				rn.releases_id AS nfoid
1195
			FROM releases r
1196
			LEFT JOIN movieinfo m ON m.id = r.movieinfo_id
1197
			LEFT JOIN usenet_groups g ON g.id = r.groups_id
1198
			LEFT JOIN categories c ON c.id = r.categories_id
1199
			LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
1200
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
1201
			%s %s",
1202
            ! empty($tags) ? ' LEFT JOIN tagging_tagged tt ON tt.taggable_id = r.id' : '',
1203
            $whereSql
1204
        );
1205
        $sql = sprintf(
1206
            '%s
1207
			ORDER BY postdate DESC
1208
			LIMIT %d OFFSET %d',
1209
            $baseSql,
1210
            $limit,
1211
            $offset
1212
        );
1213
1214
        $releases = Cache::get(md5($sql));
1215
        if ($releases !== null) {
1216
            return $releases;
1217
        }
1218
        $releases = self::fromQuery($sql);
1219
        if ($releases->isNotEmpty()) {
1220
            $releases[0]->_totalrows = $this->getPagerCount($baseSql);
1221
        }
1222
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
1223
        Cache::put(md5($sql), $releases, $expiresAt);
1224
1225
        return $releases;
1226
    }
1227
1228
    /**
1229
     * @param $currentID
1230
     * @param $name
1231
     * @param array $excludedCats
1232
     * @return array|bool
1233
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
1234
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
1235
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
1236
     */
1237
    public function searchSimilar($currentID, $name, array $excludedCats = [])
1238
    {
1239
        // Get the category for the parent of this release.
1240
        $ret = false;
1241
        $currRow = self::getCatByRelId($currentID);
1242
        if ($currRow !== null) {
1243
            $catRow = Category::find($currRow['categories_id']);
1244
            $parentCat = $catRow['root_categories_id'];
1245
1246
            $results = $this->search(['searchname' => getSimilarName($name)], -1, '', '', -1, -1, 0, config('nntmux.items_per_page'), '', -1, $excludedCats, [$parentCat]);
1247
            if (! $results) {
1248
                return $ret;
1249
            }
1250
1251
            $ret = [];
1252
            foreach ($results as $res) {
1253
                if ($res['id'] !== $currentID && $res['categoryparentid'] === $parentCat) {
1254
                    $ret[] = $res;
1255
                }
1256
            }
1257
        }
1258
1259
        return $ret;
1260
    }
1261
1262
    /**
1263
     * Get count of releases for pager.
1264
     *
1265
     *
1266
     * @param string $query The query to get the count from.
1267
     *
1268
     * @return int
1269
     */
1270
    private function getPagerCount($query): int
1271
    {
1272
        $sql = sprintf(
1273
            'SELECT COUNT(z.id) AS count FROM (%s LIMIT %s) z',
1274
            preg_replace('/SELECT.+?FROM\s+releases/is', 'SELECT r.id FROM releases', $query),
1275
            (int) config('nntmux.max_pager_results')
1276
        );
1277
        $count = Cache::get(md5($sql));
1278
        if ($count !== null) {
1279
            return $count;
1280
        }
1281
        $count = self::fromQuery($sql);
1282
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_short'));
1283
        Cache::put(md5($sql), $count[0]->count, $expiresAt);
1284
1285
        return $count[0]->count ?? 0;
1286
    }
1287
}
1288