188 lines
5.7 KiB
JavaScript
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();
|
|
});
|