From 427d855e59d13a56e19ab82893a41a7c66855e0f Mon Sep 17 00:00:00 2001 From: Brandon4466 Date: Fri, 25 Jul 2025 16:11:37 -0700 Subject: [PATCH] added touch support, changed appearance, new icons --- index.html | 228 ++++++++++++++++++++++ main-old.js | 542 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.js | 315 +++--------------------------- pause.svg | 1 + play.svg | 1 + plex.py | 27 +++ previous.svg | 1 + skip.svg | 1 + 8 files changed, 831 insertions(+), 285 deletions(-) create mode 100644 index.html create mode 100644 main-old.js create mode 100644 pause.svg create mode 100644 play.svg create mode 100644 plex.py create mode 100644 previous.svg create mode 100644 skip.svg diff --git a/index.html b/index.html new file mode 100644 index 0000000..d048a94 --- /dev/null +++ b/index.html @@ -0,0 +1,228 @@ + + + + + Now Playing + + + +
+
+ + +
+
+ Album Art +
+
Loading...
+
 
+
+ + + +
+
+
+ + + diff --git a/main-old.js b/main-old.js new file mode 100644 index 0000000..78664b0 --- /dev/null +++ b/main-old.js @@ -0,0 +1,542 @@ +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 polling for Plex now-playing details + checkPlexNowPlaying(); + setInterval(checkPlexNowPlaying, 3000); + + // 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.loadURL('data:text/html;charset=UTF-8,' + encodeURIComponent(getMainPageHTML())); + 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 checkPlexNowPlaying() { + const plexToken = "kbGwoiA_QEGzw7MgSZrY"; // <-- replace with your Plex token + const plexHost = "spyro.corp.bbrunson.com"; // <-- adjust if needed + const url = `http://${plexHost}:32400/status/sessions?X-Plex-Token=${plexToken}`; + http.get(url, (res) => { + let data = ""; + res.on('data', chunk => data += chunk); + res.on('end', () => { + // For simplicity we check for a known tag in the XML response. + let nowPlaying = "No media playing"; + if(data.includes("MediaContainer") && data.includes("Video")) { + nowPlaying = "Plex is playing media"; + } + if(win){ + win.webContents.send('plex-update', { details: nowPlaying }); + } + }); + }).on('error', err => { + console.error("Error fetching Plex now playing:", err); + }); +} + +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(); +}); + +function getMainPageHTML() { + return ` + + + + + Now Playing + + + + +
+ Plex Now Playing: Loading... +
+ + + +
+
+
+ Album Art +
+
Loading...
+
+
+
+
+ + + +
+
+
+ “Hello, is it me you’re looking for?” +
+ +
+
+
+

+

+
+
+ + + + `; +} diff --git a/main.js b/main.js index 781e2f7..88bc52e 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,7 @@ 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; @@ -12,6 +13,33 @@ app.whenReady().then(() => { 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(); @@ -37,7 +65,7 @@ function createWindow() { win.removeMenu(); // win.webContents.openDevTools(); - win.loadURL('data:text/html;charset=UTF-8,' + encodeURIComponent(getMainPageHTML())); + win.loadFile(path.join(__dirname, 'index.html')); win.on('closed', () => { win = null; }); @@ -107,7 +135,6 @@ function checkOwnSong() { }); } - function fetchAlbumArt(title, artist) { return new Promise((resolve) => { const query = encodeURIComponent(`${artist} ${title}`); @@ -152,291 +179,9 @@ ipcMain.on('media-control', (event, command) => { ipcMain.on('minimize-app', (event) => { const window = BrowserWindow.getFocusedWindow(); if (window) window.minimize(); - }); +}); ipcMain.on('close-app', (event) => { - // If you have a reference to the BrowserWindow (e.g. mainWindow), - // you can do something like: win.close(); // or app.quit(); }); - -function getMainPageHTML() { - return ` - - - - - Now Playing - - - - - - - -
-
-
- Album Art -
-
Loading...
-
-
-
-
- - - -
-
- - -
- “Hello, is it me you’re looking for?” -
- - - -
- - - - - `; -} diff --git a/pause.svg b/pause.svg new file mode 100644 index 0000000..47a766c --- /dev/null +++ b/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/play.svg b/play.svg new file mode 100644 index 0000000..6e3ed0e --- /dev/null +++ b/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plex.py b/plex.py new file mode 100644 index 0000000..21bac54 --- /dev/null +++ b/plex.py @@ -0,0 +1,27 @@ + + + // Start polling for Plex now-playing details + checkPlexNowPlaying(); + setInterval(checkPlexNowPlaying, 3000); + +function checkPlexNowPlaying() { + const plexToken = "kbGwoiA_QEGzw7MgSZrY"; // <-- replace with your Plex token + const plexHost = "spyro.corp.bbrunson.com"; // <-- adjust if needed + const url = `http://${plexHost}:32400/status/sessions?X-Plex-Token=${plexToken}`; + http.get(url, (res) => { + let data = ""; + res.on('data', chunk => data += chunk); + res.on('end', () => { + // For simplicity we check for a known tag in the XML response. + let nowPlaying = "No media playing"; + if(data.includes("MediaContainer") && data.includes("Video")) { + nowPlaying = "Plex is playing media"; + } + if(win){ + win.webContents.send('plex-update', { details: nowPlaying }); + } + }); + }).on('error', err => { + console.error("Error fetching Plex now playing:", err); + }); +} \ No newline at end of file diff --git a/previous.svg b/previous.svg new file mode 100644 index 0000000..f5e4353 --- /dev/null +++ b/previous.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skip.svg b/skip.svg new file mode 100644 index 0000000..f428eeb --- /dev/null +++ b/skip.svg @@ -0,0 +1 @@ + \ No newline at end of file