Completed
Push — dev ( e5a31e...27c800 )
by Darko
06:50
created

Releases::apiSearch()   D

Complexity

Conditions 18
Paths 27

Size

Total Lines 89
Code Lines 68

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 2
Bugs 2 Features 0
Metric Value
eloc 68
c 2
b 2
f 0
dl 0
loc 89
ccs 0
cts 26
cp 0
rs 4.8666
cc 18
nc 27
nop 9
crap 342

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

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

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

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