1import { env } from '$env/dynamic/private';
2import { get, writable } from 'svelte/store';
3
4const DID = 'did:plc:dfl62fgb7wtjj3fcbb72naae';
5const PDS = 'https://zwsp.xyz';
6const LAST_TRACK_FILE = `${env.WEBSITE_DATA_DIR}/last_track.json`;
7
8type LastTrack = {
9 name: string;
10 artist: string;
11 album: string;
12 images: {
13 mb: string | null;
14 yt: string | null;
15 };
16 link: string | null;
17 when: number;
18 status: 'playing' | 'played';
19};
20const lastTrack = writable<LastTrack | null>(null);
21
22export const getLastTrack = async () => {
23 try {
24 const data = await Deno.readTextFile(LAST_TRACK_FILE);
25 lastTrack.set(JSON.parse(data));
26 } catch (why) {
27 console.log('could not read last track: ', why);
28 lastTrack.set(null);
29 }
30};
31
32const getTrackCoverArt = (releaseMbId: string | null | undefined, originUrl: string | null | undefined) => {
33 let mb: string | null = null;
34 let yt: string | null = null;
35
36 if (releaseMbId) mb = `https://coverartarchive.org/release/${releaseMbId}/front-250`;
37
38 try {
39 if (originUrl) {
40 let videoId: string | null = null;
41 if (originUrl.includes('youtube.com') || originUrl.includes('music.youtube.com')) {
42 videoId = new URL(originUrl).searchParams.get('v');
43 } else if (originUrl.includes('youtu.be')) {
44 videoId = originUrl.split('youtu.be/')[1]?.split('?')[0];
45 }
46 if (videoId) yt = `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`;
47 }
48 } catch {
49 }
50
51 return { mb, yt };
52};
53
54const joinArtists = (artists: any[]) => {
55 if (!artists || artists.length === 0) return null;
56 return artists.map((a) => a.artistName).join(', ');
57};
58
59export const updateNowPlayingTrack = async () => {
60 try {
61 let track: any = null;
62 let when: number = Date.now();
63 let status: 'playing' | 'played' = 'played';
64
65 try {
66 const statusRes = await fetch(
67 `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${DID}&collection=fm.teal.alpha.actor.status&rkey=self`
68 );
69 if (statusRes.ok) {
70 const statusData = await statusRes.json();
71 if (statusData.value?.item) {
72 const metadata = statusData.value;
73 track = statusData.value.item;
74 if (track.playedTime) when = new Date(track.playedTime).getTime();
75 status = ((Date.now() / 1000) >= (parseInt(metadata.time) + track.duration)) ? 'played' : 'playing';
76 }
77 }
78 } catch (err) {
79 console.log('could not fetch teal status:', err);
80 }
81
82 if (!track) return;
83
84 const data: LastTrack = {
85 name: track.trackName,
86 artist: joinArtists(track.artists) ?? 'Unknown Artist',
87 album: track.releaseName ?? 'Unknown Album',
88 images: getTrackCoverArt(track.releaseMbId, track.originUrl),
89 link: track.originUrl ?? null,
90 when: when,
91 status: status
92 };
93
94 lastTrack.set(data);
95 await Deno.writeTextFile(LAST_TRACK_FILE, JSON.stringify(data));
96 } catch (why) {
97 console.log('could not fetch teal fm: ', why);
98 }
99};
100
101export const getNowPlayingTrack = () => {
102 return get(lastTrack);
103};