Issues (995)

app/Services/BookService.php (22 issues)

1
<?php
2
3
namespace App\Services;
4
5
use App\Models\BookInfo;
6
use App\Models\Category;
7
use App\Models\Release;
8
use App\Models\Settings;
9
use Blacklight\ColorCLI;
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Support\Facades\Cache;
12
use Illuminate\Support\Facades\DB;
13
14
/**
15
 * Service class for book data fetching and processing.
16
 */
17
class BookService
18
{
19
    public bool $echooutput;
20
21
    public ?string $pubkey;
22
23
    public ?string $privkey;
24
25
    public ?string $asstag;
26
27
    public int $bookqty;
28
29
    public int $sleeptime;
30
31
    public string $imgSavePath;
32
33
    public ?string $bookreqids;
34
35
    public string $renamed;
36
37
    public array $failCache;
38
39
    protected ColorCLI $colorCli;
40
41
    /**
42
     * @throws \Exception
43
     */
44
    public function __construct()
45
    {
46
        $this->echooutput = config('nntmux.echocli');
47
        $this->colorCli = new ColorCLI;
48
49
        $this->pubkey = Settings::settingValue('amazonpubkey');
50
        $this->privkey = Settings::settingValue('amazonprivkey');
51
        $this->asstag = Settings::settingValue('amazonassociatetag');
52
        $this->bookqty = Settings::settingValue('maxbooksprocessed') !== '' ? (int) Settings::settingValue('maxbooksprocessed') : 300;
53
        $this->sleeptime = Settings::settingValue('amazonsleep') !== '' ? (int) Settings::settingValue('amazonsleep') : 1000;
54
        $this->imgSavePath = storage_path('covers/book/');
55
56
        $this->bookreqids = Category::BOOKS_EBOOK;
57
        $this->renamed = (int) Settings::settingValue('lookupbooks') === 2 ? 'AND isrenamed = 1' : '';
58
59
        $this->failCache = [];
60
    }
61
62
    /**
63
     * Get book info by ID.
64
     */
65
    public function getBookInfo(?int $id): ?Model
66
    {
67
        if ($id === null) {
68
            return null;
69
        }
70
71
        return BookInfo::query()->where('id', $id)->first();
72
    }
73
74
    /**
75
     * Get book info by name using full-text search.
76
     */
77
    public function getBookInfoByName(string $title): ?Model
78
    {
79
        $searchWords = '';
80
        $title = preg_replace(['/( - | -|\(.+\)|\(|\))/', '/[^\w ]+/'], [' ', ''], $title);
81
        $title = trim(trim(preg_replace('/\s\s+/i', ' ', $title)));
82
        foreach (explode(' ', $title) as $word) {
83
            $word = trim(rtrim(trim($word), '-'));
84
            if ($word !== '' && $word !== '-') {
85
                $word = '+'.$word;
86
                $searchWords .= sprintf('%s ', $word);
87
            }
88
        }
89
        $searchWords = trim($searchWords);
90
91
        return BookInfo::search($searchWords)->first();
92
    }
93
94
    /**
95
     * Get book range with pagination.
96
     */
97
    public function getBookRange(int $page, array $cat, int $start, int $num, string $orderBy, array $excludedCats = []): array
98
    {
99
        $page = max(1, $page);
100
        $start = max(0, $start);
101
102
        $browseby = $this->getBrowseBy();
103
        $catsrch = '';
104
        if (\count($cat) > 0 && $cat[0] !== -1) {
105
            $catsrch = Category::getCategorySearch($cat);
106
        }
107
        $exccatlist = '';
108
        if (\count($excludedCats) > 0) {
109
            $exccatlist = ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')';
110
        }
111
        $order = $this->getBookOrder($orderBy);
112
        $booksql = sprintf(
113
            "
114
				SELECT SQL_CALC_FOUND_ROWS boo.id,
115
					GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id
116
				FROM bookinfo boo
117
				LEFT JOIN releases r ON boo.id = r.bookinfo_id
118
				WHERE boo.cover = 1
119
				AND boo.title != ''
120
				AND r.passwordstatus %s
121
				%s %s %s
122
				GROUP BY boo.id
123
				ORDER BY %s %s %s",
124
            app(\App\Services\Releases\ReleaseBrowseService::class)->showPasswords(),
125
            $browseby,
126
            $catsrch,
127
            $exccatlist,
128
            $order[0],
129
            $order[1],
130
            ($start === false ? '' : ' LIMIT '.$num.' OFFSET '.$start)
131
        );
132
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
133
        $booksCache = Cache::get(md5($booksql.$page));
134
        if ($booksCache !== null) {
135
            $books = $booksCache;
136
        } else {
137
            $data = DB::select($booksql);
138
            $books = ['total' => DB::select('SELECT FOUND_ROWS() AS total'), 'result' => $data];
139
            Cache::put(md5($booksql.$page), $books, $expiresAt);
140
        }
141
        $bookIDs = $releaseIDs = [];
142
        if (\is_array($books['result'])) {
143
            foreach ($books['result'] as $book => $id) {
144
                $bookIDs[] = $id->id;
145
                $releaseIDs[] = $id->grp_release_id;
146
            }
147
        }
148
        $sql = sprintf(
149
            '
150
			SELECT
151
				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,
152
			boo.*,
153
			r.bookinfo_id,
154
			g.name AS group_name,
155
			rn.releases_id AS nfoid
156
			FROM releases r
157
			LEFT OUTER JOIN usenet_groups g ON g.id = r.groups_id
158
			LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
159
			LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
160
			INNER JOIN bookinfo boo ON boo.id = r.bookinfo_id
161
			WHERE boo.id IN (%s)
162
			AND r.id IN (%s)
163
			%s
164
			GROUP BY boo.id
165
			ORDER BY %s %s',
166
            \is_array($bookIDs) && ! empty($bookIDs) ? implode(',', $bookIDs) : -1,
167
            \is_array($releaseIDs) && ! empty($releaseIDs) ? implode(',', $releaseIDs) : -1,
168
            $catsrch,
169
            $order[0],
170
            $order[1]
171
        );
172
        $return = Cache::get(md5($sql.$page));
173
        if ($return !== null) {
174
            return $return;
175
        }
176
        $return = DB::select($sql);
177
        if (\count($return) > 0) {
178
            $return[0]->_totalcount = $books['total'][0]->total ?? 0;
179
        }
180
        Cache::put(md5($sql.$page), $return, $expiresAt);
181
182
        return $return;
183
    }
184
185
    /**
186
     * Get book order array.
187
     */
188
    public function getBookOrder(string $orderBy): array
189
    {
190
        $order = $orderBy === '' ? 'r.postdate' : $orderBy;
191
        $orderArr = explode('_', $order);
192
        $orderfield = match ($orderArr[0]) {
193
            'title' => 'boo.title',
194
            'author' => 'boo.author',
195
            'publishdate' => 'boo.publishdate',
196
            'size' => 'r.size',
197
            'files' => 'r.totalpart',
198
            'stats' => 'r.grabs',
199
            default => 'r.postdate',
200
        };
201
        $ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc';
202
203
        return [$orderfield, $ordersort];
204
    }
205
206
    /**
207
     * Get book ordering options.
208
     */
209
    public function getBookOrdering(): array
210
    {
211
        return [
212
            'title_asc',
213
            'title_desc',
214
            'posted_asc',
215
            'posted_desc',
216
            'size_asc',
217
            'size_desc',
218
            'files_asc',
219
            'files_desc',
220
            'stats_asc',
221
            'stats_desc',
222
            'releasedate_asc',
223
            'releasedate_desc',
224
            'author_asc',
225
            'author_desc',
226
        ];
227
    }
228
229
    /**
230
     * Get browse by options.
231
     */
232
    public function getBrowseByOptions(): array
233
    {
234
        return ['author' => 'author', 'title' => 'title'];
235
    }
236
237
    /**
238
     * Get browse by SQL clause.
239
     */
240
    public function getBrowseBy(): string
241
    {
242
        $browseby = ' ';
243
        foreach ($this->getBrowseByOptions() as $bbk => $bbv) {
244
            if (! empty($_REQUEST[$bbk])) {
245
                $bbs = stripslashes($_REQUEST[$bbk]);
246
                $browseby .= ' AND boo.'.$bbv.' '.'LIKE '.escapeString('%'.$bbs.'%');
247
            }
248
        }
249
250
        return $browseby;
251
    }
252
253
    /**
254
     * Update book by ID.
255
     */
256
    public function update(
257
        int $id,
258
        string $title,
259
        ?string $asin,
260
        ?string $url,
261
        ?string $author,
262
        ?string $publisher,
263
        $publishdate,
264
        int $cover
265
    ): bool {
266
        return BookInfo::query()->where('id', $id)->update([
267
            'title' => $title,
268
            'asin' => $asin,
269
            'url' => $url,
270
            'author' => $author,
271
            'publisher' => $publisher,
272
            'publishdate' => $publishdate,
273
            'cover' => $cover,
274
        ]) > 0;
275
    }
276
277
    /**
278
     * Process book releases, 1 category at a time.
279
     *
280
     * @throws \Exception
281
     */
282
    public function processBookReleases(string $groupID = '', string $guidChar = ''): void
283
    {
284
        $bookids = [];
285
        if (ctype_digit((string) $this->bookreqids)) {
286
            $bookids[] = $this->bookreqids;
287
        } else {
288
            $bookids = explode(', ', $this->bookreqids);
0 ignored issues
show
It seems like $this->bookreqids can also be of type null; 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

288
            $bookids = explode(', ', /** @scrutinizer ignore-type */ $this->bookreqids);
Loading history...
289
        }
290
291
        $total = \count($bookids);
292
        if ($total > 0) {
293
            foreach ($bookids as $i => $iValue) {
294
                $query = Release::query()
295
                    ->whereNull('bookinfo_id')
296
                    ->whereIn('categories_id', [$iValue])
297
                    ->orderByDesc('postdate')
0 ignored issues
show
'postdate' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderByDesc(). ( Ignorable by Annotation )

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

297
                    ->orderByDesc(/** @scrutinizer ignore-type */ 'postdate')
Loading history...
298
                    ->limit($this->bookqty);
299
300
                if ($guidChar !== '') {
301
                    $query->where('leftguid', 'like', $guidChar.'%');
302
                }
303
304
                if ($groupID !== '') {
305
                    $query->where('groups_id', $groupID);
306
                }
307
308
                $this->processBookReleasesHelper(
309
                    $query->get(['searchname', 'id', 'categories_id']), $iValue
310
                );
311
            }
312
        }
313
    }
314
315
    /**
316
     * Process book releases helper.
317
     *
318
     * @throws \Exception
319
     */
320
    protected function processBookReleasesHelper($res, $categoryID): void
321
    {
322
        if ($res->count() > 0) {
323
            if ($this->echooutput) {
324
                $this->colorCli->header('Processing '.$res->count().' book release(s) for categories id '.$categoryID);
325
            }
326
327
            $bookId = -2;
328
            foreach ($res as $arr) {
329
                $startTime = now()->timestamp;
330
                $usedAmazon = false;
331
                // audiobooks are also books and should be handled in an identical manor, even though it falls under a music category
332
                if ($arr['categories_id'] === (int) Category::MUSIC_AUDIOBOOK) {
333
                    // audiobook
334
                    $bookInfo = $this->parseTitle($arr['searchname'], $arr['id'], 'audiobook');
335
                } else {
336
                    // ebook
337
                    $bookInfo = $this->parseTitle($arr['searchname'], $arr['id'], 'ebook');
338
                }
339
340
                if ($bookInfo !== false) {
341
                    if ($this->echooutput) {
342
                        $this->colorCli->info('Looking up: '.$bookInfo);
343
                    }
344
345
                    // Do a local lookup first
346
                    $bookCheck = $this->getBookInfoByName($bookInfo);
347
348
                    if ($bookCheck === null && \in_array($bookInfo, $this->failCache, false)) {
349
                        // Lookup recently failed, no point trying again
350
                        if ($this->echooutput) {
351
                            $this->colorCli->info('Cached previous failure. Skipping.');
352
                        }
353
                        $bookId = -2;
354
                    } elseif ($bookCheck === null) {
355
                        $bookId = $this->updateBookInfo($bookInfo);
356
                        $usedAmazon = true;
357
                        if ($bookId === -2) {
358
                            $this->failCache[] = $bookInfo;
359
                        }
360
                    } else {
361
                        $bookId = $bookCheck['id'];
362
                    }
363
364
                    // Update release.
365
                    Release::query()->where('id', $arr['id'])->update(['bookinfo_id' => $bookId]);
366
                } else { // Could not parse release title.
367
                    Release::query()->where('id', $arr['id'])->update(['bookinfo_id' => $bookId]);
368
                    if ($this->echooutput) {
369
                        echo '.';
370
                    }
371
                }
372
                // Sleep to not flood amazon.
373
                $diff = floor((now()->timestamp - $startTime) * 1000000);
374
                if ($this->sleeptime * 1000 - $diff > 0 && $usedAmazon === true) {
375
                    usleep($this->sleeptime * 1000 - $diff);
0 ignored issues
show
$this->sleeptime * 1000 - $diff of type double is incompatible with the type integer expected by parameter $microseconds 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

375
                    usleep(/** @scrutinizer ignore-type */ $this->sleeptime * 1000 - $diff);
Loading history...
376
                }
377
            }
378
        } elseif ($this->echooutput) {
379
            $this->colorCli->header('No book releases to process for categories id '.$categoryID);
380
        }
381
    }
382
383
    /**
384
     * Parse release title.
385
     *
386
     * @return bool|string
387
     */
388
    public function parseTitle($release_name, $releaseID, $releasetype)
389
    {
390
        $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);
391
        $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);
392
393
        // remove book series from title as this gets more matches on amazon
394
        $c = preg_replace('/ - \[.+\]|\[.+\]/', '', $b);
395
396
        // remove any brackets left behind
397
        $d = preg_replace('/(\(\)|\[\])/', '', $c);
398
        $releasename = trim(preg_replace('/\s\s+/i', ' ', $d));
399
400
        // the default existing type was ebook, this handles that in the same manor as before
401
        if ($releasetype === 'ebook') {
402
            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)) {
403
                if ($this->echooutput) {
404
                    $this->colorCli->headerOver('Changing category to misc books: ').$this->colorCli->primary($releasename);
0 ignored issues
show
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

404
                    /** @scrutinizer ignore-type */ $this->colorCli->headerOver('Changing category to misc books: ').$this->colorCli->primary($releasename);
Loading history...
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...
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...
405
                }
406
                Release::query()->where('id', $releaseID)->update(['categories_id' => Category::BOOKS_UNKNOWN]);
407
408
                return false;
409
            }
410
411
            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)) {
412
                if ($this->echooutput) {
413
                    $this->colorCli->headerOver('Changing category to magazines: ').$this->colorCli->primary($releasename);
0 ignored issues
show
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

413
                    /** @scrutinizer ignore-type */ $this->colorCli->headerOver('Changing category to magazines: ').$this->colorCli->primary($releasename);
Loading history...
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...
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...
414
                }
415
                Release::query()->where('id', $releaseID)->update(['categories_id' => Category::BOOKS_MAGAZINES]);
416
417
                return false;
418
            }
419
            if (! empty($releasename) && ! preg_match('/^[a-z0-9]+$|^([0-9]+ ){1,}$|Part \d+/i', $releasename)) {
420
                return $releasename;
421
            }
422
423
            return false;
424
        }
425
        if ($releasetype === 'audiobook') {
426
            if (! empty($releasename) && ! preg_match('/^[a-z0-9]+$|^([0-9]+ ){1,}$|Part \d+/i', $releasename)) {
427
                // 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
428
                return $releasename;
429
            }
430
431
            return false;
432
        }
433
434
        return false;
435
    }
436
437
    /**
438
     * Update book info from external sources.
439
     *
440
     * @return false|int|string
441
     *
442
     * @throws \Exception
443
     */
444
    public function updateBookInfo(string $bookInfo = '', $amazdata = null)
445
    {
446
        $ri = new ReleaseImageService;
447
448
        $bookId = -2;
449
450
        $book = false;
451
        if ($bookInfo !== '') {
452
            if (! $book) {
0 ignored issues
show
The condition $book is always false.
Loading history...
453
                $this->colorCli->info('Fetching data from iTunes for '.$bookInfo);
454
                $book = $this->fetchItunesBookProperties($bookInfo);
455
            } elseif ($amazdata !== null) {
456
                $book = $amazdata;
457
            }
458
        }
459
460
        if (empty($book)) {
461
            return false;
462
        }
463
464
        $check = BookInfo::query()->where('asin', $book['asin'])->first();
465
        if ($check === null) {
466
            $bookId = BookInfo::query()->insertGetId(
467
                [
468
                    'title' => $book['title'],
469
                    'author' => $book['author'],
470
                    'asin' => $book['asin'],
471
                    'isbn' => $book['isbn'],
472
                    'ean' => $book['ean'],
473
                    'url' => $book['url'],
474
                    'salesrank' => $book['salesrank'],
475
                    'publisher' => $book['publisher'],
476
                    'publishdate' => $book['publishdate'],
477
                    'pages' => $book['pages'],
478
                    'overview' => $book['overview'],
479
                    'genre' => $book['genre'],
480
                    'cover' => $book['cover'],
481
                    'created_at' => now(),
482
                    'updated_at' => now(),
483
                ]
484
            );
485
        } else {
486
            if ($check !== null) {
487
                $bookId = $check['id'];
488
            }
489
            BookInfo::query()->where('id', $bookId)->update(
490
                [
491
                    'title' => $book['title'],
492
                    'author' => $book['author'],
493
                    'asin' => $book['asin'],
494
                    'isbn' => $book['isbn'],
495
                    'ean' => $book['ean'],
496
                    'url' => $book['url'],
497
                    'salesrank' => $book['salesrank'],
498
                    'publisher' => $book['publisher'],
499
                    'publishdate' => $book['publishdate'],
500
                    'pages' => $book['pages'],
501
                    'overview' => $book['overview'],
502
                    'genre' => $book['genre'],
503
                    'cover' => $book['cover'],
504
                ]
505
            );
506
        }
507
508
        if ($bookId && $bookId !== -2) {
509
            if ($this->echooutput) {
510
                $this->colorCli->header('Added/updated book: ');
511
                if ($book['author'] !== '') {
512
                    $this->colorCli->alternateOver('   Author: ').$this->colorCli->primary($book['author']);
0 ignored issues
show
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...
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...
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

512
                    /** @scrutinizer ignore-type */ $this->colorCli->alternateOver('   Author: ').$this->colorCli->primary($book['author']);
Loading history...
513
                }
514
                $this->colorCli->alternateOver('   Title: ').$this->colorCli->primary(' '.$book['title']);
0 ignored issues
show
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

514
                /** @scrutinizer ignore-type */ $this->colorCli->alternateOver('   Title: ').$this->colorCli->primary(' '.$book['title']);
Loading history...
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...
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...
515
                if ($book['genre'] !== 'null') {
516
                    $this->colorCli->alternateOver('   Genre: ').$this->colorCli->primary(' '.$book['genre']);
0 ignored issues
show
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...
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

516
                    /** @scrutinizer ignore-type */ $this->colorCli->alternateOver('   Genre: ').$this->colorCli->primary(' '.$book['genre']);
Loading history...
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...
517
                }
518
            }
519
520
            $book['cover'] = $ri->saveImage($bookId, $book['coverurl'], $this->imgSavePath, 250, 250);
521
        } elseif ($this->echooutput) {
522
            $this->colorCli->header('Nothing to update: ').
0 ignored issues
show
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

522
            /** @scrutinizer ignore-type */ $this->colorCli->header('Nothing to update: ').
Loading history...
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...
523
            $this->colorCli->header($book['author'].
0 ignored issues
show
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...
524
                ' - '.
525
                $book['title']);
526
        }
527
528
        return $bookId;
529
    }
530
531
    /**
532
     * Fetch book properties from iTunes.
533
     *
534
     * @return array|bool
535
     */
536
    public function fetchItunesBookProperties(string $bookInfo)
537
    {
538
        $itunes = new ItunesService();
539
        $iTunesBook = $itunes->findEbook($bookInfo);
540
541
        if ($iTunesBook === null) {
542
            $this->colorCli->notice('Could not find a match on iTunes!');
543
544
            return false;
545
        }
546
547
        $this->colorCli->info('Found matching title: '.$iTunesBook['name']);
548
549
        $book = [
550
            'title' => $iTunesBook['name'],
551
            'author' => $iTunesBook['author'],
552
            'asin' => $iTunesBook['id'],
553
            'isbn' => 'null',
554
            'ean' => 'null',
555
            'url' => $iTunesBook['store_url'],
556
            'salesrank' => '',
557
            'publisher' => '',
558
            'pages' => '',
559
            'coverurl' => ! empty($iTunesBook['cover']) ? $iTunesBook['cover'] : '',
560
            'genre' => is_array($iTunesBook['genres']) ? implode(', ', $iTunesBook['genres']) : $iTunesBook['genre'],
561
            'overview' => strip_tags($iTunesBook['description'] ?? ''),
562
            'publishdate' => $iTunesBook['release_date'],
563
        ];
564
565
        if (! empty($book['coverurl'])) {
566
            $book['cover'] = 1;
567
        } else {
568
            $book['cover'] = 0;
569
        }
570
571
        return $book;
572
    }
573
}
574
575