1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
import type { SourceModule } from "../registry";
import {
CiderConfig,
CiderPayload,
type CiderConfig as Config,
type CiderPayload as Payload,
} from "@/lib/schemas/sources/cider";
export const ciderModule: SourceModule<Config, Payload> = {
kind: "cider",
label: "Now Playing",
keyless: false,
defaultRefreshSeconds: 30,
configSchema: CiderConfig,
payloadSchema: CiderPayload,
isConfigured: (config) => config.appToken.length > 0,
async fetch({ config, signal }) {
const res = await fetch(`${config.host}/api/v1/playback/now-playing`, {
headers: { apptoken: config.appToken },
signal,
});
if (res.status === 204) return { playing: false, track: null };
if (!res.ok) throw new Error(`Cider ${res.status}`);
const j = (await res.json()) as {
status?: string;
info?: {
name?: string;
artistName?: string;
albumName?: string;
artwork?: { url?: string };
durationInMillis?: number;
currentPlaybackTime?: number;
};
};
if (j.status !== "ok" || !j.info) return { playing: false, track: null };
const info = j.info;
const artRaw = info.artwork?.url ?? null;
// Replace {w}x{h} template Apple Music uses in artwork URLs
const artworkUrl = artRaw ? artRaw.replace("{w}", "300").replace("{h}", "300") : null;
return {
playing: true,
track: {
name: info.name ?? "Unknown",
artist: info.artistName ?? "Unknown",
album: info.albumName ?? "Unknown",
artworkUrl,
durationMs: (info.durationInMillis ?? 0),
currentMs: Math.round((info.currentPlaybackTime ?? 0) * 1000),
},
};
},
};
|