Skip to content

Commit 6eb6d0b

Browse files
committed
Cleaned up UX for Album view with full track list and album metadata.
1 parent de47cb7 commit 6eb6d0b

File tree

7 files changed

+163
-60
lines changed

7 files changed

+163
-60
lines changed

classes/MusicKit.php

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public static function songDetails(string $songId, string $language = 'en-US'):
172172
}
173173

174174
// call catalog: dev token only (no music-user-token necessary)
175-
$resp = \Kirby\Http\Remote::get('https://api.music.apple.com/v1/catalog/' . rawurlencode($storefront) . '/songs/' . rawurlencode($songId), [
175+
$resp = Remote::get('https://api.music.apple.com/v1/catalog/' . rawurlencode($storefront) . '/songs/' . rawurlencode($songId), [
176176
'headers' => [
177177
'Authorization' => 'Bearer ' . $dev,
178178
'Accept' => 'application/json',
@@ -201,8 +201,10 @@ public static function songDetails(string $songId, string $language = 'en-US'):
201201
if (!empty($a['artwork']['url'])) {
202202
$img = str_replace(['{w}','{h}'], [600, 600], $a['artwork']['url']);
203203
}
204-
$seconds = isset($a['durationInMillis']) ? (int) floor($a['durationInMillis'] / 1000) : null;
205-
$duration = is_int($seconds) ? sprintf('%d:%02d', floor($seconds/60), $seconds % 60) : null;
204+
205+
$duration = isset($a['durationInMillis'])
206+
? self::ms_to_mmss((int)$a['durationInMillis'])
207+
: null;
206208

207209
// releaseYear from releaseDate
208210
$releaseDate = $a['releaseDate'] ?? null;
@@ -239,6 +241,22 @@ public static function songDetails(string $songId, string $language = 'en-US'):
239241
], 200);
240242
}
241243

244+
// helper: milliseconds to mm:ss
245+
public static function ms_to_mmss(?int $ms, bool $forceHours = false): ?string
246+
{
247+
if ($ms === null) return null;
248+
$total = (int) round($ms / 1000);
249+
$h = intdiv($total, 3600);
250+
$rem = $total % 3600;
251+
$m = intdiv($total, 60);
252+
$s = $total % 60;
253+
254+
if ($forceHours || $h > 0) {
255+
return sprintf('%d:%02d:%02d', $h, $m, $s); // H:MM:SS
256+
}
257+
return sprintf('%d:%02d', $m, $s);
258+
}
259+
242260
// get individual album details
243261
public static function albumDetails(string $albumId, string $language = 'en-US'): Response
244262
{
@@ -262,7 +280,7 @@ public static function albumDetails(string $albumId, string $language = 'en-US')
262280
}
263281

264282
// call catalog: dev token only (no music-user-token necessary)
265-
$resp = \Kirby\Http\Remote::get('https://api.music.apple.com/v1/catalog/' . rawurlencode($storefront) . '/albums/' . rawurlencode($albumId), [
283+
$resp = Remote::get('https://api.music.apple.com/v1/catalog/' . rawurlencode($storefront) . '/albums/' . rawurlencode($albumId), [
266284
'headers' => [
267285
'Authorization' => 'Bearer ' . $dev,
268286
'Accept' => 'application/json',
@@ -291,6 +309,7 @@ public static function albumDetails(string $albumId, string $language = 'en-US')
291309
if (!empty($a['artwork']['url'])) {
292310
$img = str_replace(['{w}','{h}'], [600, 600], $a['artwork']['url']);
293311
}
312+
294313
$seconds = isset($a['durationInMillis']) ? (int) floor($a['durationInMillis'] / 1000) : null;
295314
$duration = is_int($seconds) ? sprintf('%d:%02d', floor($seconds/60), $seconds % 60) : null;
296315

@@ -312,18 +331,39 @@ public static function albumDetails(string $albumId, string $language = 'en-US')
312331
$url = null;
313332
}
314333

334+
// albums tracks data
335+
$tracksRaw = $body['data'][0]['relationships']['tracks']['data'] ?? [];
336+
$tracks = array_map(function ($t) {
337+
$a = $t['attributes'] ?? [];
338+
$ms = $a['durationInMillis'] ?? null;
339+
return [
340+
'id' => $t['id'] ?? null,
341+
'number' => $a['trackNumber'] ?? null,
342+
'name' => $a['name'] ?? '',
343+
'durationMs' => $ms,
344+
'duration' => self::ms_to_mmss($ms),
345+
];
346+
}, $tracksRaw);
347+
348+
// total duration
349+
$totalDurationMs = array_sum(array_map(fn($t) => $t['durationMs'] ?? 0, $tracks));
350+
$totalDuration = self::ms_to_mmss($totalDurationMs);
351+
315352
return Response::json([
316-
'id' => $id,
317-
'name' => $a['name'] ?? '',
318-
'artistName' => $a['artistName'] ?? '',
319-
'albumName' => $a['albumName'] ?? '',
320-
'genreNames' => $a['genreNames'] ?? [],
321-
'releaseDate' => $releaseDate,
322-
'releaseYear' => $releaseYear,
323-
'url' => $url,
324-
'duration' => $duration,
325-
'image' => $img,
326-
'raw' => $body, // optional: full response payload
353+
'id' => $id,
354+
'name' => $a['name'] ?? '',
355+
'artistName' => $a['artistName'] ?? '',
356+
'genreNames' => $a['genreNames'] ?? [],
357+
'releaseDate' => $releaseDate,
358+
'releaseYear' => $releaseYear,
359+
'url' => $url,
360+
'image' => $img,
361+
'recordLabel' => $a['recordLabel'],
362+
'copyright' => $a['copyright'],
363+
'trackCount' => $a['trackCount'],
364+
'totalDuration' => $totalDuration,
365+
'tracks' => $tracks,
366+
//'raw' => $body, // optional: full response payload
327367
], 200);
328368
}
329369

index.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
'musickit' => [
7878
'label' => 'Apple Music',
7979
'icon' => 'album-filled',
80+
'breadcrumbLabel' => function () {
81+
return 'Apple Music';
82+
},
8083
'menu' => true,
8184
'link' => 'applemusic',
8285
'views' => [
@@ -109,6 +112,11 @@
109112
'action' => function ($songId) {
110113
return [
111114
'component' => 'k-musickit-song-view',
115+
'breadcrumb' => function () {
116+
return [
117+
[ 'label' => 'Song' ]
118+
];
119+
},
112120
'props' => [
113121
'songId' => $songId,
114122
'language' => option('panel.language', 'en-US')
@@ -122,8 +130,13 @@
122130
'action' => function ($albumId) {
123131
return [
124132
'component' => 'k-musickit-album-view',
133+
'breadcrumb' => function () {
134+
return [
135+
[ 'label' => 'Album' ]
136+
];
137+
},
125138
'props' => [
126-
'songId' => $albumId,
139+
'albumId' => $albumId,
127140
'language' => option('panel.language', 'en-US')
128141
]
129142
];

src/components/MusicKitAlbum.vue

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<k-panel-inside>
33
<k-view>
44
<k-header class="k-site-view-header">
5-
{{ song?.name || 'Album' }}
5+
{{ album?.name || 'Album' }}
66

77
<template #buttons>
88
<k-button v-if="album?.url" icon="headphones" :link="album.url" target="_blank" theme="blue-icon" variant="filled">Listen in Apple Music</k-button>
@@ -15,17 +15,32 @@
1515
<k-section v-else>
1616
<k-grid style="gap: 0; --columns: 12; background: var(--item-color-back); border-radius: var(--rounded); box-shadow: var(--shadow);">
1717

18-
<k-image-frame v-if="album.image" :src="song.image" :alt="album.name" ratio="1/1" back="pattern" cover="true" icon="music" style="border-radius: var(--rounded); --width: 1/2" />
18+
<k-image-frame v-if="album.image" :src="album.image" :alt="album.name" ratio="1/1" back="pattern" cover="true" icon="music" style="border-radius: var(--rounded); --width: 1/2" />
1919

2020
<k-box style="--width: 1/2">
2121
<div class="k-text" style="padding: var(--spacing-8)">
2222
<p v-if="album.artistName" class="am-albumArtist">{{ album.artistName }}</p>
23-
<p v-if="album.albumName" class="am-albumAlbum">{{ album.albumName }} ({{ album.releaseYear }})</p>
23+
<p v-if="album.name" class="am-albumAlbum">{{ album.name }}</p>
24+
<k-box v-if="album.totalDuration" icon="clock" class="am-meta am-small">{{ album.totalDuration }}</k-box>
25+
26+
<k-box v-if="album.recordLabel" icon="label" class="am-meta am-metaSmall">{{ album.recordLabel }}</k-box>
27+
<k-box v-if="album.releaseDate || album.recordLabel" icon="calendar" class="am-meta am-metaSmall">Released on {{ album.releaseDate }}</k-box>
2428
</div>
2529
</k-box>
2630

2731
</k-grid>
32+
33+
<k-items
34+
v-if="album?.tracks?.length"
35+
layout="table"
36+
:items="albumTrackAsItems"
37+
:columns="albumItemsColumns"
38+
style="border-radius: var(--rounded); margin-top: var(--spacing-1)"
39+
/>
40+
41+
<k-box v-if="album.copyright" class="am-meta am-metaSmall am-copyright">{{ album.copyright }}</k-box>
2842
</k-section>
43+
2944
</k-view>
3045
</k-panel-inside>
3146
</template>
@@ -37,18 +52,21 @@ export default {
3752
albumId: String,
3853
language: String
3954
},
40-
55+
4156
data() {
4257
return {
4358
loading: true,
4459
err: null,
4560
album: null
4661
};
4762
},
48-
63+
4964
async created() {
5065
try {
66+
console.log('[AlbumView] albumId prop:', this.albumId);
5167
const url = `/applemusic/album/${encodeURIComponent(this.albumId)}?l=${encodeURIComponent(this.language || 'en-US')}`;
68+
console.log('[AlbumView] fetch URL:', url);
69+
5270
const res = await fetch(url, {
5371
credentials: 'same-origin',
5472
headers: { 'Accept': 'application/json' }
@@ -63,32 +81,50 @@ export default {
6381
}
6482
},
6583
84+
computed: {
85+
albumTrackAsItems() {
86+
return (this.album?.tracks || []).map(t => ({
87+
// k-items friendly shape
88+
text: t.name ?? '',
89+
info: t.duration ?? '',
90+
id: t.id ?? `${t.number}-${t.name}`
91+
}));
92+
},
93+
albumItemsColumns() {
94+
// k-items will display `text` & `info` — labels here just rename headers
95+
return {
96+
text: { label: 'Track' },
97+
info: { label: 'Duration', width: '1/8', align: 'right' }
98+
};
99+
}
100+
},
101+
66102
methods: {
67103
back() { this.$go('applemusic') }
68104
},
69-
105+
70106
}
71107
</script>
72108
73109
<style>
74-
.am-songArtist {
110+
.am-albumArtist {
75111
font-size: var(--text-4xl);
76112
}
77113
78-
.am-songAlbum {
114+
.am-albumAlbum {
79115
font-size: var(--text-2xl);
80116
margin-top: var(--spacing-2);
81117
}
82118
83-
.am-songAlbum,
84-
.am-songDuration {
119+
.am-albumAlbum,
120+
.am-albumDuration {
85121
color: light-dark(var(--color-gray-650), var(--color-gray-450));
86122
}
87123
88-
.am-songDuration {margin-top: var(--spacing-2);}
124+
.am-albumDuration {margin-top: var(--spacing-2);}
89125
90-
.am-songDuration,
91-
.am-songComposer {
126+
.am-albumDuration,
127+
.am-albumComposer {
92128
font-size: var(--text-lg);
93129
}
94130
@@ -106,4 +142,9 @@ export default {
106142
color: light-dark(var(--color-gray-500), var(--color-gray-700));
107143
margin-top: var(--spacing-2);
108144
}
109-
</style>
145+
146+
.am-copyright {
147+
margin-top: var(--spacing-4);
148+
width: 50%;
149+
}
150+
</style>

0 commit comments

Comments
 (0)