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

Releases::deleteSingle()   B

Complexity

Conditions 7
Paths 20

Size

Total Lines 37
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

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

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

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

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

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

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

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