|
7 | 7 | use Scottboms\MusicKit\Utils; |
8 | 8 | use Kirby\Http\Response; |
9 | 9 | use Kirby\Http\Remote; |
| 10 | +use Kirby\Cms\Content; |
10 | 11 |
|
11 | 12 | class MusicKit |
12 | 13 | { |
@@ -403,64 +404,92 @@ public static function albumDetails(string $albumId, string $language = 'en-US') |
403 | 404 | * server-side helper for front-end snippet |
404 | 405 | * fetches recently played using shared token, caches response, |
405 | 406 | * and normalize to a render-friendly array |
| 407 | + * |
406 | 408 | * @return Array |
407 | 409 | */ |
408 | | - public static function recentForFrontend(int $limit = 12, string $language = 'en-US', int $cacheTtl = 120): array |
| 410 | + public static function recentForFrontend( |
| 411 | + int $limit = 12, |
| 412 | + string $language = 'en-US', |
| 413 | + int $cacheTtl = 120, |
| 414 | + bool $asContent = true): array |
409 | 415 | { |
410 | 416 | $cache = kirby()->cache('scottboms.applemusic'); |
411 | 417 | $cacheKey = 'am:recent:site:' . md5(json_encode([$limit, $language])); |
| 418 | + |
| 419 | + // try cache (always arrays) |
412 | 420 | if ($cacheTtl > 0 && ($cached = $cache->get($cacheKey))) { |
413 | | - return $cached; |
| 421 | + return static::toContentPayload($cached); |
414 | 422 | } |
415 | 423 |
|
| 424 | + // need a shared (site-level) Music-User-Token |
416 | 425 | $mut = Auth::readAnyToken(); |
417 | 426 | if (!$mut) { |
418 | 427 | $payload = ['items' => [], 'error' => 'Missing shared Music-User-Token (site)']; |
419 | 428 | if ($cacheTtl > 0) $cache->set($cacheKey, $payload, $cacheTtl); |
420 | | - return $payload; |
| 429 | + return static::toContentPayload($payload); |
421 | 430 | } |
422 | 431 |
|
| 432 | + // dev token + fetch from api |
423 | 433 | $opts = static::opts(); |
424 | 434 | $dev = Auth::devToken($opts); |
425 | | - $resp = static::appleGet( |
426 | | - '/v1/me/recent/played/tracks?limit=' . $limit . '&l=' . rawurlencode($language), |
427 | | - $dev, |
428 | | - $mut |
429 | | - ); |
430 | | - $json = json_decode($resp->body() ?? 'null', true); |
431 | | - |
432 | | - if (!is_array($json) || $resp->code() >= 400) { |
| 435 | + |
| 436 | + $path = '/v1/me/recent/played/tracks?limit=' . (int)$limit . '&l=' . rawurlencode($language); |
| 437 | + $res = static::appleGet($path, $dev, $mut); |
| 438 | + $json = json_decode($res->body() ?? 'null', true); |
| 439 | + |
| 440 | + if (!is_array($json) || !isset($json['data'])) { |
433 | 441 | $payload = ['items' => [], 'error' => 'Apple Music API error']; |
434 | 442 | if ($cacheTtl > 0) $cache->set($cacheKey, $payload, $cacheTtl); |
435 | | - return $payload; |
| 443 | + return static::toContentPayload($payload); |
436 | 444 | } |
437 | 445 |
|
438 | | - // normalize for rendering |
| 446 | + // normalize items to cache-friendly arrays |
439 | 447 | $items = array_map(function ($i) { |
440 | 448 | $a = $i['attributes'] ?? []; |
441 | 449 | $img = null; |
| 450 | + |
442 | 451 | if (!empty($a['artwork']['url'])) { |
443 | 452 | $img = str_replace(['{w}', '{h}'], [240, 240], $a['artwork']['url']); |
444 | 453 | } |
445 | 454 |
|
446 | | - // convert millis -> mm:ss |
447 | | - $duration = $a['durationInMillis'] ?? null; |
448 | | - $songLength = Utils::format_mmss($duration); |
| 455 | + $id = $i['id'] ?? null; |
| 456 | + $url = $a['url'] ?? null; |
| 457 | + |
| 458 | + // if the id starts with i., clear the url (internal ids) |
| 459 | + if (is_string($id) && str_starts_with($id, 'i.')) { |
| 460 | + $url = null; |
| 461 | + } |
449 | 462 |
|
450 | 463 | return [ |
451 | | - 'id' => $i['id'] ?? null, |
| 464 | + 'id' => $id, |
452 | 465 | 'name' => $a['name'] ?? '', |
453 | 466 | 'artist' => $a['artistName'] ?? '', |
454 | 467 | 'album' => $a['albumName'] ?? '', |
455 | | - 'duration' => $songLength, |
456 | | - 'releaseDate' => $a['releaseDate'] ?? null, |
457 | | - 'url' => $a['url'] ?? null, |
| 468 | + 'duration' => Utils::format_mmss($a['durationInMillis'] ?? null), |
| 469 | + 'releaseDate' => $a['releaseDate'] ?? null, // ISO 8601 (use ->toDate() in templates) |
| 470 | + 'url' => $url, |
458 | 471 | 'image' => $img, |
459 | 472 | ]; |
460 | 473 | }, $json['data'] ?? []); |
461 | 474 |
|
462 | 475 | $payload = ['items' => $items, 'error' => null]; |
463 | | - if ($cacheTtl > 0) $cache->set($cacheKey, $payload, $cacheTtl); |
| 476 | + |
| 477 | + // cache arrays |
| 478 | + if ($cacheTtl > 0) { |
| 479 | + $cache->set($cacheKey, $payload, $cacheTtl); |
| 480 | + } |
| 481 | + |
| 482 | + return static::toContentPayload($payload); |
| 483 | + } |
| 484 | + |
| 485 | + |
| 486 | + /** |
| 487 | + * convert ['items' => [array...]] to ['items' => [Content...]] |
| 488 | + * @return Array |
| 489 | + */ |
| 490 | + protected static function toContentPayload(array $payload): array |
| 491 | + { |
| 492 | + $payload['items'] = array_map(fn ($i) => new Content($i), $payload['items'] ?? []); |
464 | 493 | return $payload; |
465 | 494 | } |
466 | 495 |
|
|
0 commit comments