your personal website on atproto - mirror
blento.app
1import type { CardDefinition } from '../../../types';
2import CreateLastFMCardModal from '../CreateLastFMCardModal.svelte';
3import LastFMRecentTracksCard from './LastFMRecentTracksCard.svelte';
4import { fetchLastFM } from '../api.remote';
5
6export const LastFMRecentTracksCardDefinition = {
7 type: 'lastfmRecentTracks',
8 contentComponent: LastFMRecentTracksCard,
9 creationModalComponent: CreateLastFMCardModal,
10 createNew: (card) => {
11 card.w = 4;
12 card.mobileW = 8;
13 card.h = 3;
14 card.mobileH = 6;
15 },
16 loadData: async (items) => {
17 const allData: Record<string, unknown> = {};
18 for (const item of items) {
19 const username = item.cardData.lastfmUsername;
20 if (!username) continue;
21 try {
22 const data = await fetchLastFM({ method: 'user.getRecentTracks', user: username });
23 if (data) allData[`lastfmRecentTracks:${username}`] = data?.recenttracks?.track ?? [];
24 } catch (error) {
25 console.error('Failed to fetch Last.fm recent tracks:', error);
26 }
27 }
28 return allData;
29 },
30 loadDataServer: async (items) => {
31 const allData: Record<string, unknown> = {};
32 for (const item of items) {
33 const username = item.cardData.lastfmUsername;
34 if (!username) continue;
35 try {
36 const data = await fetchLastFM({ method: 'user.getRecentTracks', user: username });
37 if (data) allData[`lastfmRecentTracks:${username}`] = data?.recenttracks?.track ?? [];
38 } catch (error) {
39 console.error('Failed to fetch Last.fm recent tracks:', error);
40 }
41 }
42 return allData;
43 },
44 onUrlHandler: (url, item) => {
45 const username = getLastFMUsername(url);
46 if (!username) return null;
47
48 item.cardData.lastfmUsername = username;
49 item.cardData.href = `https://www.last.fm/user/${username}`;
50 item.w = 4;
51 item.mobileW = 8;
52 item.h = 3;
53 item.mobileH = 6;
54 item.cardType = 'lastfmRecentTracks';
55 return item;
56 },
57 urlHandlerPriority: 5,
58 minW: 3,
59 minH: 2,
60 canHaveLabel: true,
61 name: 'Last.fm Recent Tracks',
62 keywords: ['music', 'scrobble', 'listening', 'songs', 'lastfm', 'last.fm'],
63 groups: ['Media'],
64 icon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="m9 9 10.5-3m0 6.553v3.75a2.25 2.25 0 0 1-1.632 2.163l-1.32.377a1.803 1.803 0 1 1-.99-3.467l2.31-.66a2.25 2.25 0 0 0 1.632-2.163Zm0 0V2.25L9 5.25v10.303m0 0v3.75a2.25 2.25 0 0 1-1.632 2.163l-1.32.377a1.803 1.803 0 0 1-.99-3.467l2.31-.66A2.25 2.25 0 0 0 9 15.553Z" /></svg>`
65} as CardDefinition & { type: 'lastfmRecentTracks' };
66
67function getLastFMUsername(url: string | undefined): string | undefined {
68 if (!url) return;
69 try {
70 const parsed = new URL(url);
71 if (!/^(www\.)?last\.fm$/.test(parsed.hostname)) return undefined;
72 const segments = parsed.pathname.split('/').filter(Boolean);
73 if (segments.length >= 2 && segments[0] === 'user') {
74 return segments[1];
75 }
76 return undefined;
77 } catch {
78 return undefined;
79 }
80}