Passed
Push — dev ( 535ca7...b7c17d )
by Darko
07:22
created

Releases::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
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
				FROM releases r
344
				LEFT JOIN categories c ON c.id = r.categories_id
345
				LEFT JOIN root_categories cp ON cp.id = c.root_categories_id
346
				WHERE %s %s
347
				AND r.nzbstatus = %d
348
				AND r.categories_id BETWEEN %d AND %d
349
				AND r.passwordstatus %s
350
				%s
351
				GROUP BY r.id
352
				ORDER BY %s %s %s",
353
            $this->uSQL($userShows, 'videos_id'),
354
            (! empty($excludedCats) ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
355
            NZB::NZB_ADDED,
356
            Category::TV_ROOT,
357
            Category::TV_OTHER,
358
            $this->showPasswords(),
359
            ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : ''),
360
            $orderBy[0],
361
            $orderBy[1],
362
            ($offset === false ? '' : (' LIMIT '.$limit.' OFFSET '.$offset))
363
        );
364
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long'));
365
        $result = Cache::get(md5($sql));
366
        if ($result !== null) {
367
            return $result;
368
        }
369
        $result = self::fromQuery($sql);
370
        Cache::put(md5($sql), $result, $expiresAt);
371
372
        return $result;
373
    }
374
375
    /**
376
     * Get count for my shows page pagination.
377
     *
378
     * @param       $userShows
379
     * @param int   $maxAge
380
     * @param array $excludedCats
381
     *
382
     * @return int
383
     */
384
    public function getShowsCount($userShows, $maxAge = -1, array $excludedCats = []): int
385
    {
386
        return $this->getPagerCount(
387
            sprintf(
388
                'SELECT r.id
389
				FROM releases r
390
				WHERE %s %s
391
				AND r.nzbstatus = %d
392
				AND r.categories_id BETWEEN %d AND %d
393
				AND r.passwordstatus %s
394
				%s',
395
                $this->uSQL($userShows, 'videos_id'),
396
                (\count($excludedCats) ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''),
397
                NZB::NZB_ADDED,
398
                Category::TV_ROOT,
399
                Category::TV_OTHER,
400
                $this->showPasswords(),
401
                ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '')
402
            )
403
        );
404
    }
405
406
    /**
407
     * Delete multiple releases, or a single by ID.
408
     *
409
     * @param array|int|string $list   Array of GUID or ID of releases to delete.
410
     * @throws \Exception
411
     */
412
    public function deleteMultiple($list): void
413
    {
414
        $list = (array) $list;
415
416
        $nzb = new NZB();
417
        $releaseImage = new ReleaseImage();
418
419
        foreach ($list as $identifier) {
420
            $this->deleteSingle(['g' => $identifier, 'i' => false], $nzb, $releaseImage);
421
        }
422
    }
423
424
    /**
425
     * Deletes a single release by GUID, and all the corresponding files.
426
     *
427
     * @param array                    $identifiers ['g' => Release GUID(mandatory), 'id => ReleaseID(optional, pass
428
     *                                              false)]
429
     * @param \Blacklight\NZB          $nzb
430
     * @param \Blacklight\ReleaseImage $releaseImage
431
     *
432
     * @throws \Exception
433
     */
434
    public function deleteSingle($identifiers, NZB $nzb, ReleaseImage $releaseImage): void
435
    {
436
        // Delete NZB from disk.
437
        $nzbPath = $nzb->NZBPath($identifiers['g']);
438
        if (! empty($nzbPath)) {
439
            File::delete($nzbPath);
440
        }
441
442
        // Delete images.
443
        $releaseImage->delete($identifiers['g']);
444
445
        if (config('nntmux.elasticsearch_enabled') === true) {
446
            if ($identifiers['i'] === false) {
447
                $identifiers['i'] = Release::query()->where('guid', $identifiers['g'])->first(['id']);
448
                if ($identifiers['i'] !== null) {
449
                    $identifiers['i'] = $identifiers['i']['id'];
450
                }
451
            }
452
            if ($identifiers['i'] !== false) {
453
                $params = [
454
                    'index' => 'releases',
455
                    'id' => $identifiers['i'],
456
                ];
457
458
                \Elasticsearch::delete($params);
459
            }
460
        } else {
461
            // Delete from sphinx.
462
            $this->sphinxSearch->deleteRelease($identifiers);
463
        }
464
465
        // Delete from DB.
466
        self::whereGuid($identifiers['g'])->delete();
467
    }
468
469
    /**
470
     * @param $guids
471
     * @param $category
472
     * @param $grabs
473
     * @param $videoId
474
     * @param $episodeId
475
     * @param $anidbId
476
     * @param $imdbId
477
     * @return bool|int
478
     */
479
    public function updateMulti($guids, $category, $grabs, $videoId, $episodeId, $anidbId, $imdbId)
480
    {
481
        if (! \is_array($guids) || \count($guids) < 1) {
482
            return false;
483
        }
484
485
        $update = [
486
            'categories_id'     => $category === -1 ? 'categories_id' : $category,
487
            'grabs'          => $grabs,
488
            'videos_id'      => $videoId,
489
            'tv_episodes_id' => $episodeId,
490
            'anidbid'        => $anidbId,
491
            'imdbid'         => $imdbId,
492
        ];
493
494
        return self::query()->whereIn('guid', $guids)->update($update);
495
    }
496
497
    /**
498
     * Creates part of a query for some functions.
499
     *
500
     * @param array|Collection  $userQuery
501
     * @param string $type
502
     *
503
     * @return string
504
     */
505
    public function uSQL($userQuery, $type): string
506
    {
507
        $sql = '(1=2 ';
508
        foreach ($userQuery as $query) {
509
            $sql .= sprintf('OR (r.%s = %d', $type, $query->$type);
510
            if (! empty($query->categories)) {
511
                $catsArr = explode('|', $query->categories);
512
                if (\count($catsArr) > 1) {
513
                    $sql .= sprintf(' AND r.categories_id IN (%s)', implode(',', $catsArr));
514
                } else {
515
                    $sql .= sprintf(' AND r.categories_id = %d', $catsArr[0]);
516
                }
517
            }
518
            $sql .= ') ';
519
        }
520
        $sql .= ') ';
521
522
        return $sql;
523
    }
524
525
    /**
526
     * Function for searching on the site (by subject, searchname or advanced).
527
     *
528
     *
529
     * @param array $searchArr
530
     * @param              $groupName
531
     * @param              $sizeFrom
532
     * @param              $sizeTo
533
     * @param              $daysNew
534
     * @param              $daysOld
535
     * @param int $offset
536
     * @param int $limit
537
     * @param string|array $orderBy
538
     * @param int $maxAge
539
     * @param array $excludedCats
540
     * @param string $type
541
     * @param array $cat
542
     * @param int $minSize
543
     * @param array $tags
544
     *
545
     * @return array|Collection|mixed
546
     * @throws \Foolz\SphinxQL\Exception\ConnectionException
547
     * @throws \Foolz\SphinxQL\Exception\DatabaseException
548
     * @throws \Foolz\SphinxQL\Exception\SphinxQLException
549
     */
550
    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 = [])
551
    {
552
        $sizeRange = [
553
            1 => 1,
554
            2 => 2.5,
555
            3 => 5,
556
            4 => 10,
557
            5 => 20,
558
            6 => 30,
559
            7 => 40,
560
            8 => 80,
561
            9 => 160,
562
            10 => 320,
563
            11 => 640,
564
        ];
565
        if ($orderBy === '') {
566
            $orderBy = [];
567
            $orderBy[0] = 'postdate ';
568
            $orderBy[1] = 'desc ';
569
        } else {
570
            $orderBy = $this->getBrowseOrder($orderBy);
571
        }
572
573
        $searchFields = Arr::where($searchArr, function ($value) {
574
            return $value !== -1;
575
        });
576
577
        $phrases = [];
578
        foreach ($searchFields as $key => $value) {
579
            $phrases[] = $value;
580
        }
581
582
        if (config('nntmux.elasticsearch_enabled') === true) {
583
            $searchResult = (new ElasticSearchSiteSearch())->indexSearch($phrases, $limit);
584
        } else {
585
            $results = $this->sphinxSearch->searchIndexes('releases_rt', '', [], $searchFields);
586
587
            $searchResult = Arr::pluck($results, 'id');
588
        }
589
590
        if (empty($searchResult)) {
591
            return collect();
592
        }
593
594
        $catQuery = '';
595
        if ($type === 'basic') {
596
            $catQuery = Category::getCategorySearch($cat);
597
        } elseif ($type === 'advanced' && (int) $cat[0] !== -1) {
598
            $catQuery = sprintf('AND r.categories_id = %d', $cat[0]);
599
        }
600
        $whereSql = sprintf(
601
            'WHERE r.passwordstatus %s AND r.nzbstatus = %d %s %s %s %s %s %s %s %s %s %s %s',
602
            $this->showPasswords(),
603
            NZB::NZB_ADDED,
604
            ! empty($tags) ? " AND tt.tag_name IN ('".implode("','", $tags)."')" : '',
605
            ($maxAge > 0 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $maxAge) : ''),
606
            ((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

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

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