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
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
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...
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+ “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
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
- “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