Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions scripts/nowplaying.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ async function start() {
if (!args.includes("--no-tray") && linuxTrayAvailable()) {
tray = createLinuxTray({
url: app.url,
isFirstRun: () => Boolean(liveApp?.firstRun),
getUrl: () => liveApp.url,
script: fileURLToPath(new URL("./linux-tray.py", import.meta.url)),
icon: fileURLToPath(new URL("../assets/brand/png/icon-512.png", import.meta.url)),
onQuit: () => { void close(); },
Expand Down
8 changes: 6 additions & 2 deletions src/linux-tray.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function linuxTrayAvailable({ platform = process.platform, env = process.
// The helper sees only the icon path. Menu events are a small allowlist, not
// commands or URLs supplied by the helper. Missing desktop support leaves the
// WebUI/server running rather than turning Quit into a startup failure.
export function createLinuxTray({ url, script, icon, spawnProcess = spawn, openUrl = openLinuxWebUiUrl, onQuit = () => {}, onUnavailable = () => {}, readyTimeoutMs = 5000, killTimeoutMs = 1000, env = process.env } = {}) {
export function createLinuxTray({ url, script, icon, spawnProcess = spawn, openUrl = openLinuxWebUiUrl, onQuit = () => {}, onUnavailable = () => {}, readyTimeoutMs = 5000, killTimeoutMs = 1000, isFirstRun = () => false, getUrl = () => url, env = process.env } = {}) {
const target = new URL(url);
if (target.protocol !== "http:" || target.hostname !== "127.0.0.1" || target.username || target.password || target.pathname !== "/" || target.search || target.hash)
throw new TypeError("tray URL must be a loopback app origin");
Expand Down Expand Up @@ -60,7 +60,11 @@ export function createLinuxTray({ url, script, icon, spawnProcess = spawn, openU
if (event === "ready") settle(true);
else if (readyDone && event === "quit") { didQuit = true; onQuit(); break; }
else if (readyDone && ["open", "settings", "logs"].includes(event))
try { openUrl(event === "open" ? target.origin : `${target.origin}/${event}`); }
try {
const live = new URL(getUrl());
if (live.protocol !== "http:" || live.hostname !== "127.0.0.1" || live.username || live.password || live.pathname !== "/" || live.search || live.hash) throw new TypeError("Invalid live origin");
openUrl(isFirstRun() ? `${live.origin}/settings` : event === "open" ? live.origin : `${live.origin}/${event}`);
}
catch { /* A browser failure must not terminate the media app. */ }
}
});
Expand Down
12 changes: 12 additions & 0 deletions test/linux-tray.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,15 @@ test("real helper ignoring TERM is killed within the bounded shutdown window", {
assert.equal(signal, "SIGKILL");
} finally { child.kill("SIGKILL"); }
});

test("first-run menu opens setup until the live app is configured", async () => {
const child = fakeChild(); const opened = []; let firstRun = true; let url = "http://127.0.0.1:47832";
const tray = createLinuxTray(options(child, { isFirstRun: () => firstRun, getUrl: () => url, openUrl: (url) => opened.push(url) }));
child.stdout.write("ready\nopen\nlogs\n");
await tray.ready;
firstRun = false;
url = "http://127.0.0.1:47833";
child.stdout.write("logs\n");
assert.deepEqual(opened, ["http://127.0.0.1:47832/settings", "http://127.0.0.1:47832/settings", "http://127.0.0.1:47833/logs"]);
tray.close();
});
Loading