MusicService::parseArtist()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
dl 0
loc 26
rs 9.7
c 1
b 0
f 0
cc 4
nc 3
nop 1
1
<?php
2
3
namespace App\Services;
4
5
use App\Models\Category;
6
use App\Models\Genre;
7
use App\Models\MusicInfo;
8
use App\Models\Release;
9
use App\Models\Settings;
10
use App\Services\Releases\ReleaseBrowseService;
11
use Illuminate\Support\Facades\Cache;
12
use Illuminate\Support\Facades\DB;
13
14
/**
15
 * Music Service - Handles music browsing and lookup operations.
16
 */
17
class MusicService
18
{
19
    protected const MATCH_PERCENT = 85;
20
21
    public bool $echooutput;
22
23
    public ?string $pubkey;
24
25
    public ?string $privkey;
26
27
    public ?string $asstag;
28
29
    public int $musicqty;
30
31
    public int $sleeptime;
32
33
    public string $imgSavePath;
34
35
    public mixed $renamed;
36
37
    /**
38
     * Store names of failed lookup items.
39
     */
40
    public array $failCache;
41
42
    public function __construct()
43
    {
44
        $this->echooutput = config('nntmux.echocli');
45
46
        $this->pubkey = Settings::settingValue('amazonpubkey');
47
        $this->privkey = Settings::settingValue('amazonprivkey');
48
        $this->asstag = Settings::settingValue('amazonassociatetag');
49
        $this->musicqty = Settings::settingValue('maxmusicprocessed') !== '' ? (int) Settings::settingValue('maxmusicprocessed') : 150;
50
        $this->sleeptime = Settings::settingValue('amazonsleep') !== '' ? (int) Settings::settingValue('amazonsleep') : 1000;
51
        $this->imgSavePath = config('nntmux_settings.covers_path').'/music/';
52
        $this->renamed = (int) Settings::settingValue('lookupmusic') === 2 ? 'AND isrenamed = 1' : '';
53
54
        $this->failCache = [];
55
    }
56
57
    /**
58
     * Get music info by ID.
59
     */
60
    public function getMusicInfo(int $id): ?MusicInfo
61
    {
62
        return MusicInfo::query()->with('genre')->where('id', $id)->first();
63
    }
64
65
    /**
66
     * Get music info by name using full-text search.
67
     */
68
    public function getMusicInfoByName(string $artist, string $album): ?MusicInfo
0 ignored issues
show
Unused Code introduced by
The parameter $artist is not used and could be removed. ( Ignorable by Annotation )

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

68
    public function getMusicInfoByName(/** @scrutinizer ignore-unused */ string $artist, string $album): ?MusicInfo

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
69
    {
70
        $searchwords = '';
71
        $album = preg_replace('/( - | -|\(.+\)|\(|\))/', ' ', $album);
72
        $album = preg_replace('/[^\w ]+/', '', $album);
73
        $album = preg_replace('/(WEB|FLAC|CD)/', '', $album);
74
        $album = trim(trim(preg_replace('/\s\s+/i', ' ', $album)));
75
76
        foreach (explode(' ', $album) as $word) {
77
            $word = trim(rtrim(trim($word), '-'));
78
            if ($word !== '' && $word !== '-') {
79
                $word = '+'.$word;
80
                $searchwords .= sprintf('%s ', $word);
81
            }
82
        }
83
        $searchwords = trim($searchwords);
84
85
        return MusicInfo::search($searchwords)->first();
86
    }
87
88
    /**
89
     * Get paginated music range for browsing.
90
     */
91
    public function getMusicRange(int $page, array $cat, int $start, int $num, string $orderBy, array $excludedCats = []): mixed
92
    {
93
        $page = max(1, $page);
94
        $start = max(0, $start);
95
96
        $browseby = $this->getBrowseBy();
97
        $catsrch = '';
98
        if (\count($cat) > 0 && (int) $cat[0] !== -1) {
99
            $catsrch = Category::getCategorySearch($cat);
100
        }
101
        $exccatlist = '';
102
        if (\count($excludedCats) > 0) {
103
            $exccatlist = ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')';
104
        }
105
        $order = $this->getMusicOrder($orderBy);
106
        $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium'));
107
108
        $releaseBrowseService = new ReleaseBrowseService;
109
        $passwordClause = $releaseBrowseService->showPasswords();
110
111
        $musicSql = sprintf(
112
            "
113
            SELECT SQL_CALC_FOUND_ROWS
114
                m.id,
115
                GROUP_CONCAT(r.id ORDER BY r.postdate DESC SEPARATOR ',') AS grp_release_id
116
            FROM musicinfo m
117
            LEFT JOIN releases r ON r.musicinfo_id = m.id
118
            WHERE m.title != ''
119
            AND m.cover = 1
120
            AND r.passwordstatus %s
121
            %s %s %s
122
            GROUP BY m.id
123
            ORDER BY %s %s %s",
124
            $passwordClause,
125
            $browseby,
126
            $catsrch,
127
            $exccatlist,
128
            $order[0],
129
            $order[1],
130
            ($start === false ? '' : ' LIMIT '.$num.' OFFSET '.$start)
131
        );
132
133
        $musicCache = Cache::get(md5($musicSql.$page));
134
        if ($musicCache !== null) {
135
            $music = $musicCache;
136
        } else {
137
            $data = DB::select($musicSql);
138
            $music = ['total' => DB::select('SELECT FOUND_ROWS() AS total'), 'result' => $data];
139
            Cache::put(md5($musicSql.$page), $music, $expiresAt);
140
        }
141
142
        $musicIDs = $releaseIDs = [];
143
        if (\is_array($music['result'])) {
144
            foreach ($music['result'] as $mus => $id) {
145
                $musicIDs[] = $id->id;
146
                $releaseIDs[] = $id->grp_release_id;
147
            }
148
        }
149
150
        if (empty($musicIDs) && empty($releaseIDs)) {
151
            return collect();
152
        }
153
154
        $sql = sprintf(
155
            '
156
            SELECT
157
                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,
158
                m.*,
159
                r.musicinfo_id, r.haspreview,
160
                g.name AS group_name,
161
                rn.releases_id AS nfoid
162
            FROM releases r
163
            LEFT OUTER JOIN usenet_groups g ON g.id = r.groups_id
164
            LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id
165
            LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
166
            INNER JOIN musicinfo m ON m.id = r.musicinfo_id
167
            %s %s %s
168
            GROUP BY m.id
169
            ORDER BY %s %s',
170
            ! empty($musicIDs) ? 'WHERE m.id IN ('.implode(',', $musicIDs).')' : 'AND 1=1',
171
            (! empty($releaseIDs)) ? 'AND r.id in ('.implode(',', $releaseIDs).')' : '',
172
            $catsrch,
173
            $order[0],
174
            $order[1]
175
        );
176
177
        $return = Cache::get(md5($sql.$page));
178
        if ($return !== null) {
179
            return $return;
180
        }
181
182
        $return = MusicInfo::fromQuery($sql);
183
        if ($return->isNotEmpty()) {
184
            $return[0]->_totalcount = $music['total'][0]->total ?? 0;
185
        }
186
        Cache::put(md5($sql.$page), $return, $expiresAt);
187
188
        return $return;
189
    }
190
191
    /**
192
     * Parse order by parameter and return order field and direction.
193
     */
194
    public function getMusicOrder(string $orderBy): array
195
    {
196
        $order = ($orderBy === '') ? 'r.postdate' : $orderBy;
197
        $orderArr = explode('_', $order);
198
199
        switch ($orderArr[0]) {
200
            case 'artist':
201
                $orderfield = 'm.artist';
202
                break;
203
            case 'size':
204
                $orderfield = 'r.size';
205
                break;
206
            case 'files':
207
                $orderfield = 'r.totalpart';
208
                break;
209
            case 'stats':
210
                $orderfield = 'r.grabs';
211
                break;
212
            case 'year':
213
                $orderfield = 'm.year';
214
                break;
215
            case 'genre':
216
                $orderfield = 'm.genres_id';
217
                break;
218
            case 'posted':
219
            default:
220
                $orderfield = 'r.postdate';
221
                break;
222
        }
223
224
        $ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc';
225
226
        return [$orderfield, $ordersort];
227
    }
228
229
    /**
230
     * Get available ordering options.
231
     */
232
    public function getMusicOrdering(): array
233
    {
234
        return [
235
            'artist_asc', 'artist_desc',
236
            'posted_asc', 'posted_desc',
237
            'size_asc', 'size_desc',
238
            'files_asc', 'files_desc',
239
            'stats_asc', 'stats_desc',
240
            'year_asc', 'year_desc',
241
            'genre_asc', 'genre_desc',
242
        ];
243
    }
244
245
    /**
246
     * Get browse by options.
247
     */
248
    public function getBrowseByOptions(): array
249
    {
250
        return ['artist' => 'artist', 'title' => 'title', 'genre' => 'genres_id', 'year' => 'year'];
251
    }
252
253
    /**
254
     * Build browse by SQL clause.
255
     */
256
    public function getBrowseBy(): string
257
    {
258
        $browseby = ' ';
259
        foreach ($this->getBrowseByOptions() as $bbk => $bbv) {
260
            if (! empty($_REQUEST[$bbk])) {
261
                $bbs = stripslashes($_REQUEST[$bbk]);
262
                if (stripos($bbv, 'id') !== false) {
263
                    $browseby .= ' AND m.'.$bbv.' = '.$bbs;
264
                } else {
265
                    $browseby .= ' AND m.'.$bbv.' '.'LIKE '.escapeString('%'.$bbs.'%');
266
                }
267
            }
268
        }
269
270
        return $browseby;
271
    }
272
273
    /**
274
     * Update music info record.
275
     */
276
    public function update(
277
        int $id,
278
        string $title,
279
        ?string $asin,
280
        ?string $url,
281
        ?int $salesrank,
282
        ?string $artist,
283
        ?string $publisher,
284
        ?string $releasedate,
285
        ?string $year,
286
        ?string $tracks,
287
        int $cover,
288
        ?int $genres_id
289
    ): void {
290
        MusicInfo::query()->where('id', $id)->update([
291
            'title' => $title,
292
            'asin' => $asin,
293
            'url' => $url,
294
            'salesrank' => $salesrank,
295
            'artist' => $artist,
296
            'publisher' => $publisher,
297
            'releasedate' => $releasedate,
298
            'year' => $year,
299
            'tracks' => $tracks,
300
            'cover' => $cover,
301
            'genres_id' => $genres_id,
302
        ]);
303
    }
304
305
    /**
306
     * Update or create music info from external data.
307
     *
308
     * @throws \Exception
309
     */
310
    public function updateMusicInfo(string $title, string $year, ?array $amazdata = null): int|false
311
    {
312
        $ri = new ReleaseImageService;
313
314
        $mus = [];
315
        if ($amazdata !== null) {
316
            $mus = $amazdata;
317
        } elseif ($title !== '') {
318
            $mus = $this->fetchItunesMusicProperties($title);
319
        }
320
321
        if ($mus === false) {
322
            return false;
323
        }
324
325
        $check = MusicInfo::query()->where('asin', $mus['asin'])->first(['id']);
326
327
        if ($check === null) {
328
            $musicId = MusicInfo::query()->insertGetId([
329
                'title' => $mus['title'],
330
                'asin' => $mus['asin'],
331
                'url' => $mus['url'],
332
                'salesrank' => $mus['salesrank'],
333
                'artist' => $mus['artist'],
334
                'publisher' => $mus['publisher'],
335
                'releasedate' => $mus['releasedate'],
336
                'review' => $mus['review'],
337
                'year' => $year,
338
                'genres_id' => (int) $mus['musicgenres_id'] === -1 ? null : $mus['musicgenres_id'],
339
                'tracks' => $mus['tracks'],
340
                'created_at' => now(),
341
                'updated_at' => now(),
342
            ]);
343
            $mus['cover'] = $ri->saveImage($musicId, $mus['coverurl'], $this->imgSavePath, 250, 250);
0 ignored issues
show
Bug introduced by
It seems like $musicId can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $imgName of App\Services\ReleaseImageService::saveImage() 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

343
            $mus['cover'] = $ri->saveImage(/** @scrutinizer ignore-type */ $musicId, $mus['coverurl'], $this->imgSavePath, 250, 250);
Loading history...
344
            MusicInfo::query()->where('id', $musicId)->update(['cover' => $mus['cover']]);
345
        } else {
346
            $musicId = $check['id'];
347
            $mus['cover'] = $ri->saveImage($musicId, $mus['coverurl'], $this->imgSavePath, 250, 250);
348
            MusicInfo::query()->where('id', $musicId)->update([
349
                'title' => $mus['title'],
350
                'asin' => $mus['asin'],
351
                'url' => $mus['url'],
352
                'salesrank' => $mus['salesrank'],
353
                'artist' => $mus['artist'],
354
                'publisher' => $mus['publisher'],
355
                'releasedate' => $mus['releasedate'],
356
                'review' => $mus['review'],
357
                'year' => $year,
358
                'genres_id' => (int) $mus['musicgenres_id'] === -1 ? null : $mus['musicgenres_id'],
359
                'tracks' => $mus['tracks'],
360
                'cover' => $mus['cover'],
361
            ]);
362
        }
363
364
        if ($musicId) {
365
            if ($this->echooutput) {
366
                cli()->header(
367
                    PHP_EOL.'Added/updated album: '.PHP_EOL.
368
                    '   Artist: '.$mus['artist'].PHP_EOL.
369
                    '   Title:  '.$mus['title'].PHP_EOL.
370
                    '   Year:   '.$year
371
                );
372
            }
373
            $mus['cover'] = $ri->saveImage($musicId, $mus['coverurl'], $this->imgSavePath, 250, 250);
374
        } elseif ($this->echooutput) {
375
            if ($mus['artist'] === '') {
376
                $artist = '';
377
            } else {
378
                $artist = 'Artist: '.$mus['artist'].', Album: ';
379
            }
380
381
            cli()->headerOver(
382
                'Nothing to update: '.$artist.$mus['title'].' ('.$year.')'
383
            );
384
        }
385
386
        return $musicId;
387
    }
388
389
    /**
390
     * Process music releases and lookup metadata.
391
     *
392
     * @throws \Exception
393
     */
394
    public function processMusicReleases(bool $local = false): void
395
    {
396
        $res = DB::select(
397
            sprintf(
398
                '
399
                SELECT searchname, id
400
                FROM releases
401
                WHERE musicinfo_id IS NULL
402
                %s
403
                AND categories_id IN (%s, %s, %s)
404
                ORDER BY postdate DESC
405
                LIMIT %d',
406
                $this->renamed,
407
                Category::MUSIC_MP3,
408
                Category::MUSIC_LOSSLESS,
409
                Category::MUSIC_OTHER,
410
                $this->musicqty
411
            )
412
        );
413
414
        if (! empty($res)) {
415
            foreach ($res as $arr) {
416
                $startTime = now();
417
                $usedAmazon = false;
418
                $album = $this->parseArtist($arr->searchname);
419
420
                if ($album !== false) {
421
                    $newname = $album['name'].' ('.$album['year'].')';
422
423
                    if ($this->echooutput) {
424
                        cli()->info('Looking up: '.$newname);
425
                    }
426
427
                    // Do a local lookup first
428
                    $musicCheck = $this->getMusicInfoByName('', $album['name']);
429
430
                    if ($musicCheck === null && \in_array($album['name'].$album['year'], $this->failCache, false)) {
431
                        // Lookup recently failed, no point trying again
432
                        if ($this->echooutput) {
433
                            cli()->headerOver('Cached previous failure. Skipping.');
434
                        }
435
                        $albumId = -2;
436
                    } elseif ($musicCheck === null && $local === false) {
437
                        $albumId = $this->updateMusicInfo($album['name'], $album['year']);
438
                        $usedAmazon = true;
439
                        if ($albumId === false) {
440
                            $albumId = -2;
441
                            $this->failCache[] = $album['name'].$album['year'];
442
                        }
443
                    } else {
444
                        $albumId = $musicCheck['id'];
445
                    }
446
                    Release::query()->where('id', $arr->id)->update(['musicinfo_id' => $albumId]);
447
                } else {
448
                    // No album found.
449
                    Release::query()->where('id', $arr->id)->update(['musicinfo_id' => -2]);
450
                    echo '.';
451
                }
452
453
                // Sleep to not flood the API.
454
                $sleeptime = $this->sleeptime / 1000;
455
                $diff = now()->diffInSeconds($startTime, true);
456
                if ($sleeptime - $diff > 0 && $usedAmazon === true) {
457
                    sleep($sleeptime - $diff);
458
                }
459
            }
460
461
            if ($this->echooutput) {
462
                echo PHP_EOL;
463
            }
464
        } elseif ($this->echooutput) {
465
            cli()->header('No music releases to process.');
466
        }
467
    }
468
469
    /**
470
     * Parse artist and album name from release name.
471
     *
472
     * @return array|false
473
     */
474
    public function parseArtist(string $releaseName): array|false
475
    {
476
        if (preg_match('/(.+?)(\d{1,2} \d{1,2} )?\(?(19\d{2}|20[0-1][\d])\b/', $releaseName, $name)) {
477
            $result = [];
478
            $result['year'] = $name[3];
479
480
            $a = preg_replace('/([ |-])(\d{1,2} \d{1,2} )?(Bootleg|Boxset|Clean.+Version|Compiled by.+|\dCD|Digipak|DIRFIX|DVBS|FLAC|(Ltd )?(Deluxe|Limited|Special).+Edition|Promo|PROOF|Reissue|Remastered|REPACK|RETAIL(.+UK)?|SACD|Sampler|SAT|Summer.+Mag|UK.+Import|Deluxe.+Version|VINYL|WEB)/i', ' ', $name[1]);
481
            $b = preg_replace('/([ |-])([a-z]+[\d]+[a-z]+[\d]+.+|[a-z]{2,}[\d]{2,}?.+|3FM|B00[a-z0-9]+|BRC482012|H056|UXM1DW086|(4WCD|ATL|bigFM|CDP|DST|ERE|FIM|MBZZ|MSOne|MVRD|QEDCD|RNB|SBD|SFT|ZYX)([ |-])\d.+)/i', ' ', $a);
482
            $c = preg_replace('/([ |-])(\d{1,2} \d{1,2} )?([A-Z])( ?$)|\(?[\d]{8,}\)?|([ |-])(CABLE|FREEWEB|LINE|MAG|MCD|YMRSMILES)|\(([a-z]{2,}[\d]{2,}|ost)\)|-web-/i', ' ', $b);
483
            $d = preg_replace('/VA([ |-])/', 'Various Artists ', $c);
484
            $e = preg_replace('/([ |-])(\d{1,2} \d{1,2} )?(DAB|DE|DVBC|EP|FIX|IT|Jap|NL|PL|(Pure )?FM|SSL|VLS)([ |-])/i', ' ', $d);
485
            $f = preg_replace('/([ |-])(\d{1,2} \d{1,2} )?(CABLE|CD(A|EP|M|R|S)?|QEDCD|SAT|SBD)([ |-])/i', ' ', $e);
486
            $g = str_replace(['_', '-'], ' ', $f);
487
            $h = trim(preg_replace('/\s\s+/', ' ', $g));
488
            $newname = trim(preg_replace('/ [a-z]{2}$| [a-z]{3} \d{2,}$|\d{5,} \d{5,}$|-WEB$/i', '', $h));
489
490
            if (! preg_match('/^[a-z0-9]+$/i', $newname) && strlen($newname) > 10) {
491
                $result['name'] = $newname;
492
493
                return $result;
494
            }
495
496
            return false;
497
        }
498
499
        return false;
500
    }
501
502
    /**
503
     * Match browse node ID to genre name.
504
     *
505
     * @return string|false
506
     */
507
    public function matchBrowseNode(string $nodeId): string|false
508
    {
509
        $str = '';
510
511
        // music nodes above mp3 download nodes
512
        switch ($nodeId) {
513
            case '163420':
514
                $str = 'Music Video & Concerts';
515
                break;
516
            case '30':
517
            case '624869011':
518
                $str = 'Alternative Rock';
519
                break;
520
            case '31':
521
            case '624881011':
522
                $str = 'Blues';
523
                break;
524
            case '265640':
525
            case '624894011':
526
                $str = 'Broadway & Vocalists';
527
                break;
528
            case '173425':
529
            case '624899011':
530
                $str = "Children's Music";
531
                break;
532
            case '173429': // christian
533
            case '2231705011': // gospel
534
            case '624905011': // christian & gospel
535
                $str = 'Christian & Gospel';
536
                break;
537
            case '67204':
538
            case '624916011':
539
                $str = 'Classic Rock';
540
                break;
541
            case '85':
542
            case '624926011':
543
                $str = 'Classical';
544
                break;
545
            case '16':
546
            case '624976011':
547
                $str = 'Country';
548
                break;
549
            case '7': // dance & electronic
550
            case '624988011': // dance & dj
551
                $str = 'Dance & Electronic';
552
                break;
553
            case '32':
554
            case '625003011':
555
                $str = 'Folk';
556
                break;
557
            case '67207':
558
            case '625011011':
559
                $str = 'Hard Rock & Metal';
560
                break;
561
            case '33': // world music
562
            case '625021011': // international
563
                $str = 'World Music';
564
                break;
565
            case '34':
566
            case '625036011':
567
                $str = 'Jazz';
568
                break;
569
            case '289122':
570
            case '625054011':
571
                $str = 'Latin Music';
572
                break;
573
            case '36':
574
            case '625070011':
575
                $str = 'New Age';
576
                break;
577
            case '625075011':
578
                $str = 'Opera & Vocal';
579
                break;
580
            case '37':
581
            case '625092011':
582
                $str = 'Pop';
583
                break;
584
            case '39':
585
            case '625105011':
586
                $str = 'R&B';
587
                break;
588
            case '38':
589
            case '625117011':
590
                $str = 'Rap & Hip-Hop';
591
                break;
592
            case '40':
593
            case '625129011':
594
                $str = 'Rock';
595
                break;
596
            case '42':
597
            case '625144011':
598
                $str = 'Soundtracks';
599
                break;
600
            case '35':
601
            case '625061011':
602
                $str = 'Miscellaneous';
603
                break;
604
        }
605
606
        return ($str !== '') ? $str : false;
607
    }
608
609
    /**
610
     * Fetch music properties from iTunes.
611
     *
612
     * @return array|false
613
     */
614
    protected function fetchItunesMusicProperties(string $title): array|false
615
    {
616
        // Load genres.
617
        $defaultGenres = (new GenreService)->loadGenres((string) GenreService::MUSIC_TYPE);
0 ignored issues
show
Bug introduced by
The type App\Services\GenreService was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
618
619
        $itunes = new ItunesService;
620
621
        // Try to find album first
622
        $album = $itunes->findAlbum($title);
623
624
        if ($album === null) {
625
            // Try finding a track instead
626
            $track = $itunes->findTrack($title);
627
            if ($track === null) {
628
                return false;
629
            }
630
            // Use track info to build album-like data
631
            $album = [
632
                'name' => $track['album'] ?? $track['name'],
633
                'id' => $track['album_id'] ?? $track['id'],
634
                'artist' => $track['artist'],
635
                'artist_id' => $track['artist_id'],
636
                'genre' => $track['genre'],
637
                'release_date' => $track['release_date'],
638
                'cover' => $track['cover'],
639
                'store_url' => $track['store_url'],
640
            ];
641
        }
642
643
        $genreName = $album['genre'] ?? '';
644
645
        if (! empty($genreName)) {
646
            if (\in_array(strtolower($genreName), $defaultGenres, false)) {
647
                $genreKey = array_search(strtolower($genreName), $defaultGenres, false);
648
            } else {
649
                $genreKey = Genre::query()->insertGetId(['title' => $genreName, 'type' => GenreService::MUSIC_TYPE]);
650
            }
651
        } else {
652
            $genreKey = -1;
653
        }
654
655
        // Get artist name - either from album data or lookup
656
        $artistName = $album['artist'] ?? '';
657
        if (empty($artistName) && ! empty($album['artist_id'])) {
658
            $artistData = $itunes->lookupArtist($album['artist_id']);
659
            $artistName = $artistData['artistName'] ?? '';
660
        }
661
662
        return [
663
            'title' => $album['name'],
664
            'asin' => $album['id'],
665
            'url' => $album['store_url'] ?? '',
666
            'salesrank' => '',
667
            'artist' => $artistName,
668
            'publisher' => $album['copyright'] ?? '',
669
            'releasedate' => $album['release_date'],
670
            'review' => '',
671
            'coverurl' => $album['cover'],
672
            'tracks' => $album['track_count'] ?? '',
673
            'musicgenre' => $genreName,
674
            'musicgenres_id' => $genreKey,
675
        ];
676
    }
677
}
678
679