Files
spotify-gui-electron/main.js
2025-07-25 16:11:37 -07:00

188 lines
5.7 KiB
JavaScript

const { app, BrowserWindow, ipcMain, screen } = require('electron');
const { exec } = require('child_process');
const https = require('https');
const http = require('http');
const path = require('path');
let win;
let lastSong = "";
let lastPaused = true;
app.whenReady().then(() => {
createWindow();
checkNowPlaying();
setInterval(checkNowPlaying, 2500);
// Start an HTTP server to receive notification requests (POST /notify)
http.createServer((req, res) => {
if(req.method === 'POST' && req.url === '/notify'){
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
try {
const notifData = JSON.parse(body);
if(win){
win.webContents.send('notification', notifData);
}
res.writeHead(200, {"Content-Type": "application/json"});
res.end(JSON.stringify({status:"ok"}));
} catch(e){
console.error("Notification parse error:", e);
res.writeHead(400);
res.end();
}
});
} else {
res.writeHead(404);
res.end();
}
}).listen(3000, () => {
console.log("Notification server listening on port 3000");
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
function createWindow() {
const displays = screen.getAllDisplays();
const externalDisplay = displays[3] || displays[0];
const x = externalDisplay.bounds.x;
const y = externalDisplay.bounds.y;
win = new BrowserWindow({
x: x,
y: y,
fullscreen: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
}
});
win.removeMenu();
// win.webContents.openDevTools();
win.loadFile(path.join(__dirname, 'index.html'));
win.on('closed', () => {
win = null;
});
}
async function checkNowPlaying() {
const result = await checkOwnSong();
if (result.updated && win) {
win.webContents.send('song-update', result.data);
}
}
function checkOwnSong() {
return new Promise((resolve) => {
exec(`powershell -ExecutionPolicy Bypass -File get-media.ps1`, async (error, stdout) => {
if (error) {
console.error("PowerShell error:", error);
resolve({ updated: false });
return;
}
try {
const data = JSON.parse(stdout);
// If there's NO valid data => user is paused/stopped
if (!data.title && !data.artist) {
// If we weren't already paused, now we are => update
if (!lastPaused) {
lastPaused = true;
resolve({
updated: true,
data: { paused: true }
});
} else {
// Still paused, no change
resolve({ updated: false });
}
} else {
// We DO have valid song data => the user is playing something
const currentSong = `${data.title} - ${data.artist}`;
// If we were paused, or the song changed, send an update
if (lastPaused || currentSong !== lastSong) {
lastPaused = false;
lastSong = currentSong;
const albumArt = await fetchAlbumArt(data.title, data.artist);
resolve({
updated: true,
data: {
paused: false,
title: data.title,
artist: data.artist,
albumArt
}
});
} else {
// No change in paused/playing state, no change in track
resolve({ updated: false });
}
}
} catch (err) {
console.error("JSON parse error:", stdout, err);
resolve({ updated: false });
}
});
});
}
function fetchAlbumArt(title, artist) {
return new Promise((resolve) => {
const query = encodeURIComponent(`${artist} ${title}`);
const url = `https://itunes.apple.com/search?term=${query}&limit=1`;
https.get(url, (res) => {
let body = '';
res.on('data', chunk => (body += chunk));
res.on('end', () => {
try {
const results = JSON.parse(body).results;
if (results.length > 0) {
// Replace 100x100 with 300x300 for better quality
const art = results[0].artworkUrl100.replace('100x100bb', '300x300bb');
resolve(art);
} else {
resolve("https://via.placeholder.com/100");
}
} catch (e) {
console.error("Album art fetch error:", e);
resolve("https://via.placeholder.com/100");
}
});
}).on('error', () => {
resolve("https://via.placeholder.com/100");
});
});
}
ipcMain.on('media-control', (event, command) => {
if (command) {
exec(`MediaControl.exe ${command}`, (error, stdout, stderr) => {
if (error) {
console.error(`Error running MediaControl.exe:`, error);
} else {
console.log(`Media command executed: ${stdout || command}`);
}
});
}
});
ipcMain.on('minimize-app', (event) => {
const window = BrowserWindow.getFocusedWindow();
if (window) window.minimize();
});
ipcMain.on('close-app', (event) => {
win.close();
// or app.quit();
});