@pulso/companion 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +82 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -45,6 +45,54 @@ function runShell(cmd, timeout = 1e4) {
|
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
|
+
var spotifyToken = null;
|
|
49
|
+
var spotifyTokenExpiry = 0;
|
|
50
|
+
async function getSpotifyToken() {
|
|
51
|
+
if (spotifyToken && Date.now() < spotifyTokenExpiry) return spotifyToken;
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetch("https://open.spotify.com/embed/track/4u7EnebtmKWzUH433cf5Qv", {
|
|
54
|
+
headers: { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" }
|
|
55
|
+
});
|
|
56
|
+
const html = await res.text();
|
|
57
|
+
const match = html.match(/"accessToken":"([^"]+)"/);
|
|
58
|
+
if (!match) return null;
|
|
59
|
+
spotifyToken = match[1];
|
|
60
|
+
spotifyTokenExpiry = Date.now() + 55 * 60 * 1e3;
|
|
61
|
+
return spotifyToken;
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function spotifySearch(query) {
|
|
67
|
+
const token = await getSpotifyToken();
|
|
68
|
+
if (!token) return null;
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetch(
|
|
71
|
+
`https://api.spotify.com/v1/search?q=${encodeURIComponent(query)}&type=track&limit=1`,
|
|
72
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
73
|
+
);
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
if (res.status === 401) {
|
|
76
|
+
spotifyToken = null;
|
|
77
|
+
spotifyTokenExpiry = 0;
|
|
78
|
+
const newToken = await getSpotifyToken();
|
|
79
|
+
if (!newToken) return null;
|
|
80
|
+
const retry = await fetch(
|
|
81
|
+
`https://api.spotify.com/v1/search?q=${encodeURIComponent(query)}&type=track&limit=1`,
|
|
82
|
+
{ headers: { Authorization: `Bearer ${newToken}` } }
|
|
83
|
+
);
|
|
84
|
+
if (!retry.ok) return null;
|
|
85
|
+
const retryData = await retry.json();
|
|
86
|
+
return retryData.tracks?.items?.[0]?.uri ?? null;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const data = await res.json();
|
|
91
|
+
return data.tracks?.items?.[0]?.uri ?? null;
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
48
96
|
async function handleCommand(command, params) {
|
|
49
97
|
try {
|
|
50
98
|
switch (command) {
|
|
@@ -112,8 +160,40 @@ async function handleCommand(command, params) {
|
|
|
112
160
|
case "search_play": {
|
|
113
161
|
const query = params.query;
|
|
114
162
|
if (!query) return { success: false, error: "Missing search query" };
|
|
163
|
+
const trackUri = await spotifySearch(query);
|
|
164
|
+
if (trackUri) {
|
|
165
|
+
await runAppleScript(`tell application "Spotify" to play track "${trackUri}"`);
|
|
166
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
167
|
+
try {
|
|
168
|
+
const track = await runAppleScript('tell application "Spotify" to name of current track');
|
|
169
|
+
const artist = await runAppleScript('tell application "Spotify" to artist of current track');
|
|
170
|
+
return { success: true, data: { searched: query, nowPlaying: `${track} - ${artist}`, state: "playing" } };
|
|
171
|
+
} catch {
|
|
172
|
+
return { success: true, data: { searched: query, note: "Playing track" } };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
115
175
|
await runShell(`open "spotify:search:${encodeURIComponent(query)}"`);
|
|
116
|
-
return { success: true, data: { searched: query, note: "Opened Spotify search
|
|
176
|
+
return { success: true, data: { searched: query, note: "Opened Spotify search (API unavailable). Select a song to play." } };
|
|
177
|
+
}
|
|
178
|
+
case "volume": {
|
|
179
|
+
const level = params.level;
|
|
180
|
+
if (level === void 0 || level < 0 || level > 100) return { success: false, error: "Volume must be 0-100" };
|
|
181
|
+
await runAppleScript(`tell application "Spotify" to set sound volume to ${level}`);
|
|
182
|
+
return { success: true, data: { volume: level } };
|
|
183
|
+
}
|
|
184
|
+
case "shuffle": {
|
|
185
|
+
const enabled = params.enabled;
|
|
186
|
+
await runAppleScript(`tell application "Spotify" to set shuffling to ${enabled ? "true" : "false"}`);
|
|
187
|
+
return { success: true, data: { shuffling: enabled } };
|
|
188
|
+
}
|
|
189
|
+
case "repeat": {
|
|
190
|
+
const mode = params.mode;
|
|
191
|
+
if (!mode) return { success: false, error: "Missing mode (off, context, track)" };
|
|
192
|
+
const repeatMap = { off: "off", context: "context", track: "track" };
|
|
193
|
+
const val = repeatMap[mode];
|
|
194
|
+
if (!val) return { success: false, error: "Mode must be: off, context, or track" };
|
|
195
|
+
await runAppleScript(`tell application "Spotify" to set repeating to ${val !== "off"}`);
|
|
196
|
+
return { success: true, data: { repeating: mode } };
|
|
117
197
|
}
|
|
118
198
|
default:
|
|
119
199
|
return { success: false, error: `Unknown Spotify action: ${action}` };
|
|
@@ -254,7 +334,7 @@ function scheduleReconnect() {
|
|
|
254
334
|
}
|
|
255
335
|
console.log("");
|
|
256
336
|
console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
257
|
-
console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.
|
|
337
|
+
console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.4 \u2551");
|
|
258
338
|
console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
259
339
|
console.log("");
|
|
260
340
|
connect();
|