Completed
Push — dev ( 9d36ef...8486bf )
by Darko
08:36
created

Books::processBookReleases()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 6
nop 0
dl 0
loc 20
ccs 0
cts 12
cp 0
crap 20
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace Blacklight;
4
5
use ApaiIO\ApaiIO;
6
use GuzzleHttp\Client;
7
use App\Models\Release;
8
use App\Models\BookInfo;
9
use App\Models\Category;
10
use App\Models\Settings;
11
use ApaiIO\Operations\Search;
12
use DariusIII\ItunesApi\iTunes;
13
use ApaiIO\Configuration\Country;
14
use ApaiIO\Request\GuzzleRequest;
15
use Illuminate\Support\Facades\DB;
16
use Illuminate\Support\Facades\Log;
17
use Illuminate\Support\Facades\Cache;
18
use GuzzleHttp\Exception\ServerException;
19
use ApaiIO\Configuration\GenericConfiguration;
20
use ApaiIO\ResponseTransformer\XmlToSimpleXmlObject;
21
use DariusIII\ItunesApi\Exceptions\EbookNotFoundException;
22
use DariusIII\ItunesApi\Exceptions\SearchNoResultsException;
23
24
/**
25
 * Class Books.
26
 */
27
class Books
28
{
29
    /**
30
     * @var bool
31
     */
32
    public $echooutput;
33
34
    /**
35
     * @var null|string
36
     */
37
    public $pubkey;
38
39
    /**
40
     * @var null|string
41
     */
42
    public $privkey;
43
44
    /**
45
     * @var null|string
46
     */
47
    public $asstag;
48
49
    /**
50
     * @var int|null|string
51
     */
52
    public $bookqty;
53
54
    /**
55
     * @var int|null|string
56
     */
57
    public $sleeptime;
58
59
    /**
60
     * @var string
61
     */
62
    public $imgSavePath;
63
64
    /**
65
     * @var null|string
66
     */
67
    public $bookreqids;
68
69
    /**
70
     * @var string
71
     */
72
    public $renamed;
73
74
    /**
75
     * @var array
76
     */
77
    public $failCache;
78
79
    /**
80
     * @var \Blacklight\ColorCLI
81
     */
82
    protected $colorCli;
83
84
    /**
85
     * @param array $options Class instances / Echo to cli.
86
     *
87
     * @throws \Exception
88
     */
89
    public function __construct(array $options = [])
90
    {
91
        $defaults = [
92
            'Echo'     => false,
93
            'Settings' => null,
94
        ];
95
        $options += $defaults;
96
97
        $this->echooutput = ($options['Echo'] && config('nntmux.echocli'));
98
99
        $this->colorCli = new ColorCLI();
100
101
        $this->pubkey = Settings::settingValue('APIs..amazonpubkey');
102
        $this->privkey = Settings::settingValue('APIs..amazonprivkey');
103
        $this->asstag = Settings::settingValue('APIs..amazonassociatetag');
104
        $this->bookqty = Settings::settingValue('..maxbooksprocessed') !== '' ? (int) Settings::settingValue('..maxbooksprocessed') : 300;
0 ignored issues
show
introduced by
The condition App\Models\Settings::set...booksprocessed') !== '' is always true.
Loading history...
105
        $this->sleeptime = Settings::settingValue('..amazonsleep') !== '' ? (int) Settings::settingValue('..amazonsleep') : 1000;
0 ignored issues
show
introduced by
The condition App\Models\Settings::set...'..amazonsleep') !== '' is always true.
Loading history...
106
        $this->imgSavePath = NN_COVERS.'book'.DS;
107
        $result = Settings::settingValue('..book_reqids');
108
        $this->bookreqids = $result ?? Category::BOOKS_EBOOK;
109
        $this->renamed = (int) Settings::settingValue('..lookupbooks') === 2 ? 'AND isrenamed = 1' : '';
110
111
        $this->failCache = [];
112
    }
113
114
    /**
115
     * @param $id
116
     *
117
     * @return \Illuminate\Database\Eloquent\Model|null|static
118
     */
119
    public function getBookInfo($id)
120
    {
121
        return BookInfo::query()->where('id', $id)->first();
122
    }
123
124
    /**
125
     * @param $title
126
     *
127
     * @return \Illuminate\Database\Eloquent\Model
128
     */
129
    public function getBookInfoByName($title)
130
    {
131
132
        //only used to get a count of words
133
        $searchWords = '';
134
        $title = preg_replace('/( - | -|\(.+\)|\(|\))/', ' ', $title);
135
        $title = preg_replace('/[^\w ]+/', '', $title);
136
        $title = trim(trim(preg_replace('/\s\s+/i', ' ', $title)));
137
        foreach (explode(' ', $title) as $word) {
138
            $word = trim(rtrim(trim($word), '-'));
139
            if ($word !== '' && $word !== '-') {
140
                $word = '+'.$word;
141
                $searchWords .= sprintf('%s ', $word);
142
            }
143
        }
144
        $searchWords = trim($searchWords);
145
146
        return BookInfo::search($searchWords)->first();
147
    }
148
149
    /**
150
     * @param       $page
151
     * @param       $cat
152
     * @param       $start
153
     * @param       $num
154
     * @param       $orderby
155
     * @param array $excludedcats
156
     *
157
     * @return array
158
     * @throws \Exception
159
     */
160
    public function getBookRange($page, $cat, $start, $num, $orderby, array $excludedcats = []): array
161
    {
162
        $browseby = $this->getBrowseBy();
163
        $catsrch = '';
164
        if (\count($cat) > 0 && $cat[0] !== -1) {
165
            $catsrch = Category::getCategorySearch($cat);
166
        }
167
        $exccatlist = '';
168
        if (\count($excludedcats) > 0) {
169
            $exccatlist = ' AND r.categories_id NOT IN ('.implode(',', $excludedcats).')';
170
        }
171
        $order = $this->getBookOrder($orderby);
172
        $booksql = sprintf(
173
            "
174
				SELECT SQL_CALC_FOUND_ROWS boo.id,
175
					GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id
176
				FROM bookinfo boo
177
				LEFT JOIN releases r ON boo.id = r.bookinfo_id
178
				WHERE r.nzbstatus = 1
179
				AND boo.cover = 1
180
				AND boo.title != ''
181
				AND r.passwordstatus %s
182
				%s %s %s
183
				GROUP BY boo.id
184
				ORDER BY %s %s %s",
185
            (new Releases())->showPasswords(),
186
            $browseby,
187
            $catsrch,
188
            $exccatlist,
189
            $order[0],
190
            $order[1],
191
            ($start === false ? '' : ' LIMIT '.$num.' OFFSET '.$start)
192
        );
193
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
194
        $bookscache = Cache::get(md5($booksql.$page));
195
        if ($bookscache !== null) {
196
            $books = $bookscache;
197
        } else {
198
            $data = DB::select($booksql);
199
            $books = ['total' => DB::select('SELECT FOUND_ROWS() AS total'), 'result' => $data];
200
            Cache::put(md5($booksql.$page), $books, $expiresAt);
201
        }
202
        $bookIDs = $releaseIDs = false;
203
        if (\is_array($books['result'])) {
204
            foreach ($books['result'] as $book => $id) {
205
                $bookIDs[] = $id->id;
206
                $releaseIDs[] = $id->grp_release_id;
207
            }
208
        }
209
        $sql = sprintf(
210
            '
211
			SELECT
212
				r.id, r.rarinnerfilecount, r.grabs, r.comments, r.totalpart, r.size, r.postdate, r.searchname, r.haspreview, r.passwordstatus, r.guid, df.failed AS failed,
213
			boo.*,
214
			r.bookinfo_id,
215
			g.name AS group_name,
216
			rn.releases_id AS nfoid
217
			FROM releases r
218
			LEFT OUTER JOIN groups g ON g.id = r.groups_id
219
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
220
			LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
221
			INNER JOIN bookinfo boo ON boo.id = r.bookinfo_id
222
			WHERE boo.id IN (%s)
223
			AND r.id IN (%s)
224
			%s
225
			GROUP BY boo.id
226
			ORDER BY %s %s',
227
            (\is_array($bookIDs) ? implode(',', $bookIDs) : -1),
0 ignored issues
show
introduced by
The condition is_array($bookIDs) is always false.
Loading history...
228
            (\is_array($releaseIDs) ? implode(',', $releaseIDs) : -1),
0 ignored issues
show
introduced by
The condition is_array($releaseIDs) is always false.
Loading history...
229
            $catsrch,
230
            $order[0],
231
            $order[1]
232
        );
233
        $return = Cache::get(md5($sql.$page));
234
        if ($return !== null) {
235
            return $return;
236
        }
237
        $return = DB::select($sql);
238
        if (\count($return) > 0) {
239
            $return[0]->_totalcount = $books['total'][0]->total ?? 0;
240
        }
241
        Cache::put(md5($sql.$page), $return, $expiresAt);
242
243
        return $return;
244
    }
245
246
    /**
247
     * @param $orderby
248
     *
249
     * @return array
250
     */
251
    public function getBookOrder($orderby): array
252
    {
253
        $order = $orderby === '' ? 'r.postdate' : $orderby;
254
        $orderArr = explode('_', $order);
255
        switch ($orderArr[0]) {
256
            case 'title':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
257
                $orderfield = 'boo.title';
258
                break;
259
            case 'author':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
260
                $orderfield = 'boo.author';
261
                break;
262
            case 'publishdate':
263
                $orderfield = 'boo.publishdate';
264
                break;
265
            case 'size':
266
                $orderfield = 'r.size';
267
                break;
268
            case 'files':
269
                $orderfield = 'r.totalpart';
270
                break;
271
            case 'stats':
272
                $orderfield = 'r.grabs';
273
                break;
274
            case 'posted':
275
            default:
276
                $orderfield = 'r.postdate';
277
                break;
278
        }
279
        $ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc';
280
281
        return [$orderfield, $ordersort];
282
    }
283
284
    /**
285
     * @return array
286
     */
287
    public function getBookOrdering(): array
288
    {
289
        return [
290
            'title_asc',
291
            'title_desc',
292
            'posted_asc',
293
            'posted_desc',
294
            'size_asc',
295
            'size_desc',
296
            'files_asc',
297
            'files_desc',
298
            'stats_asc',
299
            'stats_desc',
300
            'releasedate_asc',
301
            'releasedate_desc',
302
            'author_asc',
303
            'author_desc',
304
        ];
305
    }
306
307
    /**
308
     * @return array
309
     */
310
    public function getBrowseByOptions(): array
311
    {
312
        return ['author' => 'author', 'title' => 'title'];
313
    }
314
315
    /**
316
     * @return string
317
     */
318
    public function getBrowseBy(): string
319
    {
320
        $browseby = ' ';
321
        foreach ($this->getBrowseByOptions() as $bbk => $bbv) {
322
            if (isset($_REQUEST[$bbk]) && ! empty($_REQUEST[$bbk])) {
323
                $bbs = stripslashes($_REQUEST[$bbk]);
324
                $browseby .= 'AND boo.'.$bbv.' '.'LIKE '.escapeString('%'.$bbs.'%');
325
            }
326
        }
327
328
        return $browseby;
329
    }
330
331
    /**
332
     * @param $title
333
     *
334
     * @return false|mixed,
335
     */
336
    public function fetchAmazonBookProperties($title)
337
    {
338
        $conf = new GenericConfiguration();
339
        $client = new Client();
340
        $request = new GuzzleRequest($client);
341
342
        $book = [];
343
344
        try {
345
            $conf
346
                ->setCountry(Country::INTERNATIONAL)
347
                ->setAccessKey($this->pubkey)
348
                ->setSecretKey($this->privkey)
349
                ->setAssociateTag($this->asstag)
350
                ->setRequest($request)
351
                ->setResponseTransformer(new XmlToSimpleXmlObject());
352
        } catch (\Throwable $e) {
353
            if (config('app.debug') === true) {
354
                Log::error($e->getMessage());
355
            }
356
        } catch (ServerException $e) {
357
            if (config('app.debug') === true) {
358
                Log::error($e->getMessage());
359
            }
360
        }
361
362
        $search = new Search();
363
        $search->setCategory('Books');
364
        $search->setKeywords($title);
365
        $search->setResponseGroup(['Large']);
366
367
        $response = (new ApaiIO($conf))->runOperation($search);
368
        if ($response === false) {
369
            throw new \RuntimeException('Could not connect to Amazon');
370
        }
371
372
        if (isset($response->Items->Item->ItemAttributes->Title)) {
373
            $this->colorCli->info('Found matching title: '.$response->Items->Item->ItemAttributes->Title);
374
375
            $book['title'] = (string) $response->Items->Item->ItemAttributes->Title;
376
            $book['author'] = (string) $response->Items->Item->ItemAttributes->Author;
377
            $book['asin'] = (string) $response->Items->Item->ASIN;
378
            $book['isbn'] = (string) $response->Items->Item->ItemAttributes->ISBN;
379
            if ($book['isbn'] === '') {
380
                $book['isbn'] = 'null';
381
            }
382
383
            $book['ean'] = (string) $response->Items->Item->ItemAttributes->EAN;
384
            if ($book['ean'] === '') {
385
                $book['ean'] = 'null';
386
            }
387
388
            $book['url'] = (string) $response->Items->Item->DetailPageURL;
389
            $book['url'] = str_replace('%26tag%3Dws', '%26tag%3Dopensourceins%2D21', $book['url']);
390
391
            $book['salesrank'] = (string) $response->Items->Item->SalesRank;
392
            if ($book['salesrank'] === '') {
393
                $book['salesrank'] = 'null';
394
            }
395
396
            $book['publisher'] = (string) $response->Items->Item->ItemAttributes->Publisher;
397
            if ($book['publisher'] === '') {
398
                $book['publisher'] = 'null';
399
            }
400
401
            $book['publishdate'] = date('Y-m-d', strtotime((string) $response->Items->Item->ItemAttributes->PublicationDate));
402
            if ($book['publishdate'] === '') {
403
                $book['publishdate'] = 'null';
404
            }
405
406
            $book['pages'] = (string) $response->Items->Item->ItemAttributes->NumberOfPages;
407
            if ($book['pages'] === '') {
408
                $book['pages'] = 'null';
409
            }
410
411
            if (isset($response->Items->Item->EditorialReviews->EditorialReview->Content)) {
412
                $book['overview'] = strip_tags((string) $response->Items->Item->EditorialReviews->EditorialReview->Content);
413
                if ($book['overview'] === '') {
414
                    $book['overview'] = 'null';
415
                }
416
            } else {
417
                $book['overview'] = 'null';
418
            }
419
420
            if (isset($response->Items->Item->BrowseNodes->BrowseNode->Name)) {
421
                $book['genre'] = (string) $response->Items->Item->BrowseNodes->BrowseNode->Name;
422
                if ($book['genre'] === '') {
423
                    $book['genre'] = 'null';
424
                }
425
            } else {
426
                $book['genre'] = 'null';
427
            }
428
429
            $book['coverurl'] = (string) $response->Items->Item->LargeImage->URL;
430
            if ($book['coverurl'] !== '') {
431
                $book['cover'] = 1;
432
            } else {
433
                $book['cover'] = 0;
434
            }
435
436
            return $book;
437
        }
438
439
        $this->colorCli->notice('Could not find a match on Amazon!');
440
441
        return false;
442
    }
443
444
    /**
445
     * Process book releases, 1 category at a time.
446
     *
447
     * @throws \Exception
448
     */
449
    public function processBookReleases(): void
450
    {
451
        $bookids = [];
452
        if (ctype_digit((string) $this->bookreqids)) {
453
            $bookids[] = $this->bookreqids;
454
        } else {
455
            $bookids = explode(', ', $this->bookreqids);
456
        }
457
458
        $total = \count($bookids);
459
        if ($total > 0) {
460
            foreach ($bookids as $i => $iValue) {
461
                $this->processBookReleasesHelper(
462
                    Release::query()->where('nzbstatus', '=', NZB::NZB_ADDED)
463
                        ->whereNull('bookinfo_id')
464
                        ->whereIn('categories_id', [$bookids[$i]])
465
                    ->orderBy('postdate', 'desc')
466
                    ->limit($this->bookqty)
0 ignored issues
show
Bug introduced by
It seems like $this->bookqty can also be of type string; however, parameter $value of Illuminate\Database\Query\Builder::limit() does only seem to accept integer, 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

466
                    ->limit(/** @scrutinizer ignore-type */ $this->bookqty)
Loading history...
467
                    ->get(['searchname', 'id', 'categories_id']),
468
                    $bookids[$i]
469
                );
470
            }
471
        }
472
    }
473
474
    /**
475
     * @param $res
476
     * @param $categoryID
477
     *
478
     * @throws \Exception
479
     */
480
    protected function processBookReleasesHelper($res, $categoryID): void
481
    {
482
        if ($res->count() > 0) {
483
            if ($this->echooutput) {
484
                $this->colorCli->header('Processing '.$res->count().' book release(s) for categories id '.$categoryID);
485
            }
486
487
            $bookId = -2;
488
            foreach ($res as $arr) {
489
                $startTime = now()->timestamp;
490
                $usedAmazon = false;
491
                // audiobooks are also books and should be handled in an identical manor, even though it falls under a music category
492
                if ($arr['categories_id'] === '3030') {
493
                    // audiobook
494
                    $bookInfo = $this->parseTitle($arr['searchname'], $arr['id'], 'audiobook');
495
                } else {
496
                    // ebook
497
                    $bookInfo = $this->parseTitle($arr['searchname'], $arr['id'], 'ebook');
498
                }
499
500
                if ($bookInfo !== false) {
501
                    if ($this->echooutput) {
502
                        $this->colorCli->headerOver('Looking up: ').$this->colorCli->primary($bookInfo);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->headerOver('Looking up: ') targeting Blacklight\ColorCLI::headerOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->primary($bookInfo) targeting Blacklight\ColorCLI::primary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->headerOver('Looking up: ') of type void can be used in concatenation? ( Ignorable by Annotation )

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

502
                        /** @scrutinizer ignore-type */ $this->colorCli->headerOver('Looking up: ').$this->colorCli->primary($bookInfo);
Loading history...
503
                    }
504
505
                    // Do a local lookup first
506
                    $bookCheck = $this->getBookInfoByName($bookInfo);
507
508
                    if ($bookCheck === null && \in_array($bookInfo, $this->failCache, false)) {
509
                        // Lookup recently failed, no point trying again
510
                        if ($this->echooutput) {
511
                            $this->colorCli->headerOver('Cached previous failure. Skipping.');
512
                        }
513
                        $bookId = -2;
514
                    } elseif ($bookCheck === null) {
515
                        $bookId = $this->updateBookInfo($bookInfo);
516
                        $usedAmazon = true;
517
                        if ($bookId === -2) {
518
                            $this->failCache[] = $bookInfo;
519
                        }
520
                    } elseif ($bookCheck !== null) {
521
                        $bookId = $bookCheck['id'];
522
                    }
523
524
                    // Update release.
525
                    Release::query()->where('id', $arr['id'])->update(['bookinfo_id' => $bookId]);
526
                } else { // Could not parse release title.
527
                    Release::query()->where('id', $arr['id'])->update(['bookinfo_id' => $bookId]);
528
                    if ($this->echooutput) {
529
                        echo '.';
530
                    }
531
                }
532
                // Sleep to not flood amazon.
533
                $diff = floor((now()->timestamp - $startTime) * 1000000);
534
                if ($this->sleeptime * 1000 - $diff > 0 && $usedAmazon === true) {
535
                    usleep($this->sleeptime * 1000 - $diff);
0 ignored issues
show
Bug introduced by
$this->sleeptime * 1000 - $diff of type double is incompatible with the type integer expected by parameter $micro_seconds of usleep(). ( Ignorable by Annotation )

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

535
                    usleep(/** @scrutinizer ignore-type */ $this->sleeptime * 1000 - $diff);
Loading history...
536
                }
537
            }
538
        } elseif ($this->echooutput) {
539
            $this->colorCli->header('No book releases to process for categories id '.$categoryID);
540
        }
541
    }
542
543
    /**
544
     * @param $release_name
545
     * @param $releaseID
546
     * @param $releasetype
547
     *
548
     * @return bool|string
549
     */
550
    public function parseTitle($release_name, $releaseID, $releasetype)
551
    {
552
        $a = preg_replace('/\d{1,2} \d{1,2} \d{2,4}|(19|20)\d\d|anybody got .+?[a-z]\? |[-._ ](Novel|TIA)([-._ ]|$)|([ \.])HQ([-\. ])|[\(\)\.\-_ ](AVI|AZW3?|DOC|EPUB|LIT|MOBI|NFO|RETAIL|(si)?PDF|RTF|TXT)[\)\]\.\-_ ](?![a-z0-9])|compleet|DAGSTiDNiNGEN|DiRFiX|\+ extra|r?e ?Books?([\.\-_ ]English|ers)?|azw3?|ePu([bp])s?|html|mobi|^NEW[\.\-_ ]|PDF([\.\-_ ]English)?|Please post more|Post description|Proper|Repack(fix)?|[\.\-_ ](Chinese|English|French|German|Italian|Retail|Scan|Swedish)|^R4 |Repost|Skytwohigh|TIA!+|TruePDF|V413HAV|(would someone )?please (re)?post.+? "|with the authors name right/i', '', $release_name);
553
        $b = preg_replace('/^(As Req |conversion |eq |Das neue Abenteuer \d+|Fixed version( ignore previous post)?|Full |Per Req As Found|(\s+)?R4 |REQ |revised |version |\d+(\s+)?$)|(COMPLETE|INTERNAL|RELOADED| (AZW3|eB|docx|ENG?|exe|FR|Fix|gnv64|MU|NIV|R\d\s+\d{1,2} \d{1,2}|R\d|Req|TTL|UC|v(\s+)?\d))(\s+)?$/i', '', $a);
554
555
        //remove book series from title as this gets more matches on amazon
556
        $c = preg_replace('/ - \[.+\]|\[.+\]/', '', $b);
557
558
        //remove any brackets left behind
559
        $d = preg_replace('/(\(\)|\[\])/', '', $c);
560
        $releasename = trim(preg_replace('/\s\s+/i', ' ', $d));
561
562
        // the default existing type was ebook, this handles that in the same manor as before
563
        if ($releasetype === 'ebook') {
564
            if (preg_match('/^([a-z0-9] )+$|ArtofUsenet|ekiosk|(ebook|mobi).+collection|erotica|Full Video|ImwithJamie|linkoff org|Mega.+pack|^[a-z0-9]+ (?!((January|February|March|April|May|June|July|August|September|O([ck])tober|November|De([cz])ember)))[a-z]+( (ebooks?|The))?$|NY Times|(Book|Massive) Dump|Sexual/i', $releasename)) {
565
                if ($this->echooutput) {
566
                    $this->colorCli->headerOver('Changing category to misc books: ').$this->colorCli->primary($releasename);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->primary($releasename) targeting Blacklight\ColorCLI::primary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->headerO...egory to misc books: ') targeting Blacklight\ColorCLI::headerOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->headerO...egory to misc books: ') of type void can be used in concatenation? ( Ignorable by Annotation )

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

566
                    /** @scrutinizer ignore-type */ $this->colorCli->headerOver('Changing category to misc books: ').$this->colorCli->primary($releasename);
Loading history...
567
                }
568
                Release::query()->where('id', $releaseID)->update(['categories_id' => Category::BOOKS_UNKNOWN]);
569
570
                return false;
571
            }
572
573
            if (preg_match('/^([a-z0-9ü!]+ ){1,2}(N|Vol)?\d{1,4}([abc])?$|^([a-z0-9]+ ){1,2}(Jan( |unar|$)|Feb( |ruary|$)|Mar( |ch|$)|Apr( |il|$)|May(?![a-z0-9])|Jun([ e$])|Jul([ y$])|Aug( |ust|$)|Sep( |tember|$)|O([ck])t( |ober|$)|Nov( |ember|$)|De([cz])( |ember|$))/ui', $releasename) && ! preg_match('/Part \d+/i', $releasename)) {
574
                if ($this->echooutput) {
575
                    $this->colorCli->headerOver('Changing category to magazines: ').$this->colorCli->primary($releasename);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->headerO...tegory to magazines: ') targeting Blacklight\ColorCLI::headerOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->headerO...tegory to magazines: ') of type void can be used in concatenation? ( Ignorable by Annotation )

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

575
                    /** @scrutinizer ignore-type */ $this->colorCli->headerOver('Changing category to magazines: ').$this->colorCli->primary($releasename);
Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->primary($releasename) targeting Blacklight\ColorCLI::primary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
576
                }
577
                Release::query()->where('id', $releaseID)->update(['categories_id' => Category::BOOKS_MAGAZINES]);
578
579
                return false;
580
            }
581
            if (! empty($releasename) && ! preg_match('/^[a-z0-9]+$|^([0-9]+ ){1,}$|Part \d+/i', $releasename)) {
582
                return $releasename;
583
            }
584
585
            return false;
586
        }
587
        if ($releasetype === 'audiobook') {
588
            if (! empty($releasename) && ! preg_match('/^[a-z0-9]+$|^([0-9]+ ){1,}$|Part \d+/i', $releasename)) {
589
                // we can skip category for audiobooks, since we already know it, so as long as the release name is valid return it so that it is postprocessed by amazon.  In the future, determining the type of audiobook could be added (Lecture or book), since we can skip lookups on lectures, but for now handle them all the same way
590
                return $releasename;
591
            }
592
593
            return false;
594
        }
595
596
        return false;
597
    }
598
599
    /**
600
     * @param string $bookInfo
601
     * @param null   $amazdata
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $amazdata is correct as it would always require null to be passed?
Loading history...
602
     *
603
     * @return false|int|string
604
     * @throws \Exception
605
     */
606
    public function updateBookInfo($bookInfo = '', $amazdata = null)
607
    {
608
        $ri = new ReleaseImage();
609
610
        $bookId = -2;
611
612
        $book = false;
613
        if ($bookInfo !== '') {
614
            /*$this->colorCli->info('Fetching data from Amazon for '.$bookInfo);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
615
616
            $book = $this->fetchAmazonBookProperties($bookInfo);*/
617
618
            if (! $book) {
0 ignored issues
show
introduced by
The condition $book is always false.
Loading history...
619
                $this->colorCli->info('Fetching data from iTunes for '.$bookInfo);
620
                $book = $this->fetchItunesBookProperties($bookInfo);
621
            } elseif ($amazdata !== null) {
622
                $book = $amazdata;
623
            }
624
        }
625
626
        if (empty($book)) {
627
            return false;
628
        }
629
630
        $check = BookInfo::query()->where('asin', $book['asin'])->first();
631
        if ($check === null) {
632
            $bookId = BookInfo::query()->insertGetId(
633
                [
634
                    'title' => $book['title'],
635
                    'author' => $book['author'],
636
                    'asin' => $book['asin'],
637
                    'isbn' => $book['isbn'],
638
                    'ean' => $book['ean'],
639
                    'url' => $book['url'],
640
                    'salesrank' => $book['salesrank'],
641
                    'publisher' => $book['publisher'],
642
                    'publishdate' => $book['publishdate'],
643
                    'pages' => $book['pages'],
644
                    'overview' => $book['overview'],
645
                    'genre' => $book['genre'],
646
                    'cover' => $book['cover'],
647
                    'created_at' => now(),
648
                    'updated_at' => now(),
649
                ]
650
            );
651
        } else {
652
            if ($check !== null) {
653
                $bookId = $check['id'];
654
            }
655
            BookInfo::query()->where('id', $bookId)->update(
656
                [
657
                    'title' => $book['title'],
658
                    'author' => $book['author'],
659
                    'asin' => $book['asin'],
660
                    'isbn' => $book['isbn'],
661
                    'ean' => $book['ean'],
662
                    'url' => $book['url'],
663
                    'salesrank' => $book['salesrank'],
664
                    'publisher' => $book['publisher'],
665
                    'publishdate' => $book['publishdate'],
666
                    'pages' => $book['pages'],
667
                    'overview' => $book['overview'],
668
                    'genre' => $book['genre'],
669
                    'cover' => $book['cover'],
670
                ]
671
            );
672
        }
673
674
        if ($bookId && $bookId !== -2) {
675
            if ($this->echooutput) {
676
                $this->colorCli->header('Added/updated book: ');
677
                if ($book['author'] !== '') {
678
                    $this->colorCli->alternateOver('   Author: ').$this->colorCli->primary($book['author']);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->alternateOver(' Author: ') targeting Blacklight\ColorCLI::alternateOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->alternateOver(' Author: ') of type void can be used in concatenation? ( Ignorable by Annotation )

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

678
                    /** @scrutinizer ignore-type */ $this->colorCli->alternateOver('   Author: ').$this->colorCli->primary($book['author']);
Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->primary($book['author']) targeting Blacklight\ColorCLI::primary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
679
                }
680
                $this->colorCli->alternateOver('   Title: ').$this->colorCli->primary(' '.$book['title']);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->alternateOver(' Title: ') targeting Blacklight\ColorCLI::alternateOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->primary(' ' . $book['title']) targeting Blacklight\ColorCLI::primary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->alternateOver(' Title: ') of type void can be used in concatenation? ( Ignorable by Annotation )

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

680
                /** @scrutinizer ignore-type */ $this->colorCli->alternateOver('   Title: ').$this->colorCli->primary(' '.$book['title']);
Loading history...
681
                if ($book['genre'] !== 'null') {
682
                    $this->colorCli->alternateOver('   Genre: ').$this->colorCli->primary(' '.$book['genre']);
0 ignored issues
show
Bug introduced by
Are you sure $this->colorCli->alternateOver(' Genre: ') of type void can be used in concatenation? ( Ignorable by Annotation )

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

682
                    /** @scrutinizer ignore-type */ $this->colorCli->alternateOver('   Genre: ').$this->colorCli->primary(' '.$book['genre']);
Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->alternateOver(' Genre: ') targeting Blacklight\ColorCLI::alternateOver() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure the usage of $this->colorCli->primary(' ' . $book['genre']) targeting Blacklight\ColorCLI::primary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
683
                }
684
            }
685
686
            $book['cover'] = $ri->saveImage($bookId, $book['coverurl'], $this->imgSavePath, 250, 250);
687
        } elseif ($this->echooutput) {
688
            $this->colorCli->header('Nothing to update: ').
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->header('Nothing to update: ') targeting Blacklight\ColorCLI::header() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
Are you sure $this->colorCli->header('Nothing to update: ') of type void can be used in concatenation? ( Ignorable by Annotation )

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

688
            /** @scrutinizer ignore-type */ $this->colorCli->header('Nothing to update: ').
Loading history...
689
                $this->colorCli->header($book['author'].
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->colorCli->header(...' - ' . $book['title']) targeting Blacklight\ColorCLI::header() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
690
                    ' - '.
691
                    $book['title']);
692
        }
693
694
        return $bookId;
695
    }
696
697
    /**
698
     * @param string $bookInfo
699
     * @return array|bool
700
     * @throws \DariusIII\ItunesApi\Exceptions\InvalidProviderException
701
     */
702
    public function fetchItunesBookProperties(string $bookInfo)
703
    {
704
        $book = true;
705
        try {
706
            $iTunesBook = iTunes::load('ebook')->fetchOneByName($bookInfo);
707
        } catch (EbookNotFoundException $e) {
708
            $book = false;
709
        } catch (SearchNoResultsException $e) {
710
            $book = false;
711
        }
712
713
        if ($book) {
714
            $this->colorCli->info('Found matching title: '.$iTunesBook->getName());
715
            $book = [
716
                'title' => $iTunesBook->getName(),
717
                'author' => $iTunesBook->getAuthor(),
0 ignored issues
show
Bug introduced by
The method getAuthor() does not exist on DariusIII\ItunesApi\Entities\EntityInterface. It seems like you code against a sub-type of DariusIII\ItunesApi\Entities\EntityInterface such as DariusIII\ItunesApi\Entities\Ebook. ( Ignorable by Annotation )

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

717
                'author' => $iTunesBook->/** @scrutinizer ignore-call */ getAuthor(),
Loading history...
718
                'asin' => $iTunesBook->getItunesId(),
719
                'isbn' => 'null',
720
                'ean' => 'null',
721
                'url' => $iTunesBook->getStoreUrl(),
0 ignored issues
show
Bug introduced by
The method getStoreUrl() does not exist on DariusIII\ItunesApi\Entities\EntityInterface. It seems like you code against a sub-type of said class. However, the method does not exist in DariusIII\ItunesApi\Entities\Track or DariusIII\ItunesApi\Entities\Artist. Are you sure you never get one of those? ( Ignorable by Annotation )

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

721
                'url' => $iTunesBook->/** @scrutinizer ignore-call */ getStoreUrl(),
Loading history...
722
                'salesrank' => '',
723
                'publisher' => '',
724
                'pages' => '',
725
                'coverurl' => ! empty($iTunesBook->getCover()) ? str_replace('100x100', '800x800', $iTunesBook->getCover()) : '',
0 ignored issues
show
Bug introduced by
The method getCover() does not exist on DariusIII\ItunesApi\Entities\EntityInterface. It seems like you code against a sub-type of said class. However, the method does not exist in DariusIII\ItunesApi\Entities\Track or DariusIII\ItunesApi\Entities\Artist. Are you sure you never get one of those? ( Ignorable by Annotation )

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

725
                'coverurl' => ! empty($iTunesBook->/** @scrutinizer ignore-call */ getCover()) ? str_replace('100x100', '800x800', $iTunesBook->getCover()) : '',
Loading history...
726
                'genre' => implode(', ', $iTunesBook->getGenre()),
0 ignored issues
show
Bug introduced by
The method getGenre() does not exist on DariusIII\ItunesApi\Entities\EntityInterface. It seems like you code against a sub-type of said class. However, the method does not exist in DariusIII\ItunesApi\Entities\Artist. Are you sure you never get one of those? ( Ignorable by Annotation )

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

726
                'genre' => implode(', ', $iTunesBook->/** @scrutinizer ignore-call */ getGenre()),
Loading history...
727
                'overview' => strip_tags($iTunesBook->getDescription()),
0 ignored issues
show
Bug introduced by
The method getDescription() does not exist on DariusIII\ItunesApi\Entities\EntityInterface. It seems like you code against a sub-type of DariusIII\ItunesApi\Entities\EntityInterface such as DariusIII\ItunesApi\Entities\Ebook or DariusIII\ItunesApi\Entities\Movie. ( Ignorable by Annotation )

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

727
                'overview' => strip_tags($iTunesBook->/** @scrutinizer ignore-call */ getDescription()),
Loading history...
728
                'publishdate' => $iTunesBook->getReleaseDate()->format('Y-m-d'),
0 ignored issues
show
Bug introduced by
The method getReleaseDate() does not exist on DariusIII\ItunesApi\Entities\EntityInterface. It seems like you code against a sub-type of said class. However, the method does not exist in DariusIII\ItunesApi\Entities\Track or DariusIII\ItunesApi\Entities\Artist. Are you sure you never get one of those? ( Ignorable by Annotation )

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

728
                'publishdate' => $iTunesBook->/** @scrutinizer ignore-call */ getReleaseDate()->format('Y-m-d'),
Loading history...
729
            ];
730
            if (! empty($book['coverurl'])) {
731
                $book['cover'] = 1;
732
            } else {
733
                $book['cover'] = 0;
734
            }
735
        } else {
736
            $this->colorCli->notice('Could not find a match on iTunes!');
737
        }
738
739
        return $book;
740
    }
741
}
742