Pipris is an extensible MPRIS scrobbler written with Deno.
1const TEN_MINUTES_MS = 10 * 60 * 1000;
2
3/**
4 * Build the data object expected by the stub module.
5 *
6 * @param {object} metadata Raw MPRIS Metadata dict
7 * @param {string} playbackStatus "Playing" | "Paused" | "Stopped"
8 * @param {number} positionUs Current position in microseconds
9 * @param {string} playedTime ISO timestamp of when the track was first seen
10 * @returns {object|null} Formatted data, or null if metadata is missing
11 */
12export function formatTrackData(metadata, playbackStatus, positionUs, playedTime) {
13 const title = metaString(metadata, "xesam:title");
14 if (!title) return null;
15
16 const artistList = metaStringArray(metadata, "xesam:artist");
17 const album = metaString(metadata, "xesam:album") ?? "";
18 const artUrl = metaString(metadata, "mpris:artUrl") ?? "";
19 const lengthUs = metaInt(metadata, "mpris:length") ?? 0;
20
21 const now = Date.now();
22 const positionMs = Math.floor(positionUs / 1000);
23 const lengthMs = Math.floor(lengthUs / 1000);
24
25 let expiry;
26 if (playbackStatus === "Paused") {
27 expiry = new Date(now + TEN_MINUTES_MS).toISOString();
28 } else {
29 const remainingMs = Math.max(0, lengthMs - positionMs);
30 expiry = new Date(now + remainingMs).toISOString();
31 }
32
33 return {
34 artists: artistList.map((name) => ({ artistName: name })),
35 trackName: title,
36 playbackStatus,
37 playedTime,
38 releaseName: album,
39 artUrl,
40 duration: Math.round(lengthMs / 1000),
41 expiry,
42 };
43}
44
45// --- helpers for reading dbus-next Variant values ----
46
47function metaString(metadata, key) {
48 const v = metadata[key];
49 if (!v) return undefined;
50 return typeof v.value === "string" ? v.value : String(v.value);
51}
52
53function metaStringArray(metadata, key) {
54 const v = metadata[key];
55 if (!v) return [];
56 const arr = v.value;
57 return Array.isArray(arr) ? arr.map(String) : [String(arr)];
58}
59
60function metaInt(metadata, key) {
61 const v = metadata[key];
62 if (!v) return undefined;
63 const n = Number(v.value);
64 return Number.isFinite(n) ? n : undefined;
65}