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: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "streambert",
"version": "2.5.0",
"version": "2.6.0",
"description": "Streambert Desktop App",
"author": {
"name": "truelockmc",
Expand Down
9 changes: 9 additions & 0 deletions preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,13 @@ contextBridge.exposeInMainWorld("electron", {
},
offScheduledBackupRequested: (h) =>
ipcRenderer.removeListener("scheduled-backup-requested", h),

fetchReleaseImage: (url) =>
ipcRenderer.invoke("fetch-release-image", { url }),
});

if (process.platform === "darwin") {
navigator.mediaDevices?.addEventListener("devicechange", () => {
ipcRenderer.send("audio-device-changed");
});
}
63 changes: 61 additions & 2 deletions src/ipc/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ function register(getMainWindow, { writeSecretMigration }) {
: ["/usr/bin/mpv", "/usr/local/bin/mpv", "/snap/bin/mpv", "mpv"];

// ── Validate subtitle paths ───────────────────────────────────────────
// Each subtitle path is independently validated
// Each subtitle path is independently validated.
const subFilePaths = Array.isArray(subtitlePaths)
? subtitlePaths
.map((sp) => (typeof sp === "string" ? sp : sp?.path))
Expand Down Expand Up @@ -325,6 +325,7 @@ function register(getMainWindow, { writeSecretMigration }) {
}

// Same check for every trusted source (GitHub, Codeberg, ...)
// see TRUSTED_UPDATE_SOURCES above.
const trustedSource = findTrustedUpdateSource(parsed);
if (!trustedSource) {
return { ok: false, error: "Unauthorized update source" };
Expand Down Expand Up @@ -568,6 +569,7 @@ function register(getMainWindow, { writeSecretMigration }) {
// ── Proxy release-note images through the main process ───────────────────
// Codeberg (and GitHub) release images are blocked by Electron's renderer
// CSP. Fetch them here in the main process and return a base64 data-URI.

const ALLOWED_IMAGE_HOSTS = new Set([
"codeberg.org",
"github.com",
Expand Down Expand Up @@ -691,4 +693,61 @@ function register(getMainWindow, { writeSecretMigration }) {
});
}

module.exports = { register };
// ── Central audio-device-change recovery (macOS HDMI/TV fix) ─────────────────
// When the user switches audio output (e.g. Speakers → HDMI TV), Chromium
// suspends every AudioContext in every session.

function registerAudioDeviceRecovery() {
if (process.platform !== "darwin") return; // only needed on macOS

const { session, webContents, ipcMain: ipc } = require("electron");

const RECOVERY_JS = `
(() => {
async function recoverAudio() {
const ctxs = window.__audioContexts || [];
for (const ctx of ctxs) {
try { if (ctx.state === 'suspended') await ctx.resume(); } catch {}
}
const v = document.querySelector('video');
if (!v || v.paused) return;
const t = v.currentTime;
try {
v.pause();
await new Promise(r => setTimeout(r, 80));
v.currentTime = t;
await v.play();
} catch {}
}
recoverAudio();
})()
`;

const recoverAllWebContents = () => {
for (const wc of webContents.getAllWebContents()) {
if (wc.isDestroyed()) continue;
wc.executeJavaScript(RECOVERY_JS).catch(() => {});
try {
for (const frame of wc.mainFrame?.framesInSubtree ?? []) {
try {
frame.executeJavaScript(RECOVERY_JS);
} catch {}
}
} catch {}
}
};

// Hook 1: Chromium's built-in device-selection event
session.defaultSession.on(
"select-audio-device",
(event, details, callback) => {
callback(""); // let Chromium pick the default, don't block
setTimeout(recoverAllWebContents, 150);
},
);

// Hook 2: renderer sends this when navigator.mediaDevices fires "devicechange"
ipc.on("audio-device-changed", () => setTimeout(recoverAllWebContents, 150));
}

module.exports = { register, registerAudioDeviceRecovery };
14 changes: 14 additions & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,20 @@ body.no-anim *::after {
display: none !important;
}

/* Fix window-dragging on MacOS */
.window-titlebar {
-webkit-app-region: drag;
cursor: move;
}

/* Ensure titlebar buttons are clickable and not part of drag region */
.window-titlebar button,
.window-titlebar .titlebar-btn {
-webkit-app-region: no-drag;
pointer-events: auto;
cursor: default;
}

/* Hide the custom window titlebar on player fullscreen */
[data-player-fullscreen] .window-titlebar {
display: none !important;
Expand Down
Loading