Pipris is an extensible MPRIS scrobbler written with Deno.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add playback status

Clay c82e80de 1ba47c77

+62 -3
+1 -1
README.md
··· 1 - # Pipris ![Version 0.0.1](https://img.shields.io/badge/version-0.0.1-blue) 1 + # Pipris ![Version 0.0.2](https://img.shields.io/badge/version-0.0.1-blue) 2 2 3 3 Pipris is an extensible MPRIS scrobbler written in Deno. It was originally created for 4 4 [teal.fm](https://teal.fm), but can be extended to support a wide variety of
+1
src/formatter.js
··· 32 32 return { 33 33 artists: artistList.map((name) => ({ artistName: name })), 34 34 trackName: title, 35 + playbackStatus, 35 36 playedTime, 36 37 releaseName: album, 37 38 duration: Math.round(lengthMs / 1000),
+19 -1
src/index.js
··· 45 45 await mod.init(config); 46 46 } 47 47 48 - modules.push({ name: file, onData: mod.onData }); 48 + modules.push({ 49 + name: file, 50 + onData: mod.onData, 51 + onClear: typeof mod.onClear === "function" ? mod.onClear : null, 52 + }); 49 53 console.log(`[tealMpris] Loaded module: ${file}`); 50 54 } catch (err) { 51 55 console.error(`[tealMpris] Failed to load ${file}: ${err.message}`); ··· 59 63 await mod.onData(data); 60 64 } catch (err) { 61 65 console.error(`[tealMpris] Module ${mod.name} error: ${err.message}`); 66 + } 67 + } 68 + } 69 + 70 + async function callModulesClear() { 71 + for (const mod of modules) { 72 + if (!mod.onClear) continue; 73 + try { 74 + await mod.onClear(); 75 + } catch (err) { 76 + console.error(`[tealMpris] Module ${mod.name} onClear error: ${err.message}`); 62 77 } 63 78 } 64 79 } ··· 146 161 cleanupWatch = null; 147 162 } 148 163 activePlayer = null; 164 + currentTrackId = null; 165 + currentPlayedTime = null; 166 + await callModulesClear(); 149 167 } 150 168 return; 151 169 }
+41 -1
src/modules/teal.js
··· 1 1 import { Client } from "@atcute/client"; 2 2 import { PasswordSession } from "@atcute/password-session"; 3 3 4 - const AGENT_STRING = "pipris/0.0.1 (https://tangled.org/clay.rip/pipris)"; 4 + const AGENT_STRING = "pipris/0.0.2 (https://tangled.org/clay.rip/pipris)"; 5 5 const PLAY_FINISH_TOLERANCE_MS = 5000; 6 6 7 7 let rpc; ··· 76 76 }); 77 77 } 78 78 79 + async function clearStatus() { 80 + const now = new Date(); 81 + const expired = new Date(now.getTime() - 60000); 82 + await rpc.post("com.atproto.repo.putRecord", { 83 + input: { 84 + repo: did, 85 + collection: "fm.teal.alpha.actor.status", 86 + rkey: "self", 87 + record: { 88 + $type: "fm.teal.alpha.actor.status", 89 + time: expired.toISOString(), 90 + expiry: expired.toISOString(), 91 + item: { 92 + trackName: "", 93 + artists: [], 94 + }, 95 + }, 96 + }, 97 + }); 98 + } 99 + 79 100 // --- Play submission --- 80 101 81 102 async function submitPlay(track) { ··· 128 149 prevTrack = { ...data }; 129 150 } 130 151 152 + if (data.playbackStatus !== "Playing") { 153 + try { 154 + await clearStatus(); 155 + } catch (err) { 156 + console.error(`[teal] Failed to clear status: ${err.message}`); 157 + } 158 + return; 159 + } 160 + 131 161 try { 132 162 await updateStatus(data); 133 163 } catch (err) { 134 164 console.error(`[teal] Failed to update status: ${err.message}`); 135 165 } 136 166 } 167 + 168 + export async function onClear() { 169 + if (!rpc) return; 170 + try { 171 + await clearStatus(); 172 + console.log("[teal] Cleared status (player gone)"); 173 + } catch (err) { 174 + console.error(`[teal] Failed to clear status: ${err.message}`); 175 + } 176 + }