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

Releases::animeSearch()   C

Complexity

Conditions 13
Paths 14

Size

Total Lines 83
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 60
c 2
b 1
f 0
dl 0
loc 83
ccs 0
cts 11
cp 0
rs 6.6166
cc 13
nc 14
nop 7
crap 182

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace Blacklight;
4
5
use App\Models\Category;
6
use App\Models\Release;
7
use App\Models\Settings;
8
use App\Models\UsenetGroup;
9
use 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