diff --git a/apps/electron-backend/src/app/events/settings.events.ts b/apps/electron-backend/src/app/events/settings.events.ts
index 5d3797880..5eb290f2c 100644
--- a/apps/electron-backend/src/app/events/settings.events.ts
+++ b/apps/electron-backend/src/app/events/settings.events.ts
@@ -1,6 +1,7 @@
import { ipcMain } from 'electron';
import { normalizeExternalPlayerArguments } from '@iptvnator/shared/interfaces';
import {
+ EMBEDDED_MPV_FRAME_COPY,
MPV_PLAYER_ARGUMENTS,
MPV_REUSE_INSTANCE,
store,
@@ -37,6 +38,11 @@ ipcMain.handle('SETTINGS_UPDATE', (_event, arg) => {
store.set(MPV_REUSE_INSTANCE, arg.mpvReuseInstance);
}
+ // Applied on the next app start (window sandbox is fixed at creation).
+ if (arg.embeddedMpvFrameCopy !== undefined) {
+ store.set(EMBEDDED_MPV_FRAME_COPY, !!arg.embeddedMpvFrameCopy);
+ }
+
if (arg.vlcReuseInstance !== undefined) {
store.set(VLC_REUSE_INSTANCE, arg.vlcReuseInstance);
}
diff --git a/apps/electron-backend/src/app/services/embedded-mpv-native.service.ts b/apps/electron-backend/src/app/services/embedded-mpv-native.service.ts
index 78a483f90..22e85be22 100644
--- a/apps/electron-backend/src/app/services/embedded-mpv-native.service.ts
+++ b/apps/electron-backend/src/app/services/embedded-mpv-native.service.ts
@@ -131,6 +131,14 @@ export class EmbeddedMpvNativeService {
return this.isFrameCopyEngineActive() ? 'frame-copy' : 'native';
}
+ isFrameCopyAvailable(): boolean {
+ return (
+ process.platform === 'darwin' &&
+ process.arch === 'arm64' &&
+ this.resolveFrameCopyHelperPath() !== null
+ );
+ }
+
getFrameSource(sessionId: string): EmbeddedMpvFrameSource | null {
return this.frameCopyAdapter?.getFrameSource(sessionId) ?? null;
}
@@ -250,6 +258,7 @@ export class EmbeddedMpvNativeService {
supported: true,
platform: process.platform,
engine: 'frame-copy',
+ frameCopyAvailable: true,
capabilities: {
subtitles: true,
playbackSpeed: true,
@@ -274,6 +283,7 @@ export class EmbeddedMpvNativeService {
supported: true,
platform: process.platform,
engine: this.getActiveEngine(),
+ frameCopyAvailable: this.isFrameCopyAvailable(),
capabilities: this.detectCapabilities(),
};
} catch (error) {
@@ -339,6 +349,7 @@ export class EmbeddedMpvNativeService {
supported: true,
platform: process.platform,
engine: this.getActiveEngine(),
+ frameCopyAvailable: this.isFrameCopyAvailable(),
capabilities: this.detectCapabilities(),
};
} catch (error) {
@@ -370,6 +381,7 @@ export class EmbeddedMpvNativeService {
supported: true,
platform: process.platform,
engine: this.getActiveEngine(),
+ frameCopyAvailable: this.isFrameCopyAvailable(),
capabilities: this.detectCapabilities(),
};
} catch (error) {
diff --git a/apps/electron-backend/src/app/services/store.service.ts b/apps/electron-backend/src/app/services/store.service.ts
index a472708aa..0a73bcaa4 100644
--- a/apps/electron-backend/src/app/services/store.service.ts
+++ b/apps/electron-backend/src/app/services/store.service.ts
@@ -8,6 +8,13 @@ export const VLC_PLAYER_PATH = 'VLC_PLAYER_PATH';
export const VLC_PLAYER_ARGUMENTS = 'VLC_PLAYER_ARGUMENTS';
export const MPV_REUSE_INSTANCE = 'MPV_REUSE_INSTANCE';
export const VLC_REUSE_INSTANCE = 'VLC_REUSE_INSTANCE';
+/**
+ * Embedded MPV frame-copy engine opt-in (macOS arm64). Lives in the main
+ * process config file because it must be readable synchronously before the
+ * BrowserWindow is created — the engine relaxes the window sandbox for its
+ * preload frame pump, which cannot change after window creation.
+ */
+export const EMBEDDED_MPV_FRAME_COPY = 'EMBEDDED_MPV_FRAME_COPY';
export type StoreType = {
[WINDOW_BOUNDS]: Electron.Rectangle;
@@ -17,6 +24,7 @@ export type StoreType = {
[VLC_PLAYER_ARGUMENTS]: string;
[MPV_REUSE_INSTANCE]: boolean;
[VLC_REUSE_INSTANCE]: boolean;
+ [EMBEDDED_MPV_FRAME_COPY]: boolean;
};
// Export singleton store instance
diff --git a/apps/electron-backend/src/main.ts b/apps/electron-backend/src/main.ts
index 84f772e1c..1d1787805 100644
--- a/apps/electron-backend/src/main.ts
+++ b/apps/electron-backend/src/main.ts
@@ -31,6 +31,10 @@ import { databaseWorkerClient } from './app/services/database-worker-client';
import WindowEvents from './app/events/window.events';
import XtreamEvents from './app/events/xtream.events';
import { environment } from './environments/environment';
+import {
+ EMBEDDED_MPV_FRAME_COPY,
+ store,
+} from './app/services/store.service';
app.setName('iptvnator');
@@ -55,6 +59,20 @@ if (electronUserDataPath) {
app.setPath('userData', electronUserDataPath);
}
+// The frame-copy embedded MPV engine must be decided before the main window
+// exists: it relaxes the window sandbox for the preload frame pump, and
+// webPreferences are fixed at window creation. The Settings toggle persists
+// to the main-process config store; an explicitly set env var (including
+// '0') always wins so dev/CI behavior stays scriptable.
+if (
+ process.platform === 'darwin' &&
+ process.arch === 'arm64' &&
+ process.env.IPTVNATOR_ENABLE_EMBEDDED_MPV_FRAME_COPY === undefined &&
+ store.get(EMBEDDED_MPV_FRAME_COPY, false)
+) {
+ process.env.IPTVNATOR_ENABLE_EMBEDDED_MPV_FRAME_COPY = '1';
+}
+
let fixPathScheduled = false;
/**
diff --git a/apps/web/src/app/settings/settings-form.utils.ts b/apps/web/src/app/settings/settings-form.utils.ts
index 19a52df6c..da7d20cef 100644
--- a/apps/web/src/app/settings/settings-form.utils.ts
+++ b/apps/web/src/app/settings/settings-form.utils.ts
@@ -72,6 +72,7 @@ export function createSettingsForm(
],
],
recordingFolder: '',
+ embeddedMpvFrameCopy: false,
coverSize: 'medium' as CoverSize,
...(supportsEpg
? {
@@ -135,6 +136,7 @@ export function createSettingsFromFormValue(
remoteControl: value.remoteControl ?? false,
remoteControlPort: Number(value.remoteControlPort ?? 8765),
recordingFolder: value.recordingFolder ?? '',
+ embeddedMpvFrameCopy: value.embeddedMpvFrameCopy ?? false,
coverSize: value.coverSize ?? 'medium',
epgUrl,
preferUploadedEpgOverXtream:
diff --git a/apps/web/src/app/settings/settings-playback-section.component.html b/apps/web/src/app/settings/settings-playback-section.component.html
index 8b3e15080..c368b3c7d 100644
--- a/apps/web/src/app/settings/settings-playback-section.component.html
+++ b/apps/web/src/app/settings/settings-playback-section.component.html
@@ -114,6 +114,42 @@
+ @if (frameCopyAvailable()) {
+
{{ 'SETTINGS.RECORDING_FOLDER_LABEL' | translate }}
diff --git a/apps/web/src/app/settings/settings-playback-section.component.ts b/apps/web/src/app/settings/settings-playback-section.component.ts
index a0d6a6531..5e8f645b7 100644
--- a/apps/web/src/app/settings/settings-playback-section.component.ts
+++ b/apps/web/src/app/settings/settings-playback-section.component.ts
@@ -45,6 +45,10 @@ export class SettingsPlaybackSectionComponent {
readonly players = input.required();
readonly streamFormatEnum = input.required();
readonly isDesktop = input(false);
+ /** Frame-copy embedded MPV engine is possible on this machine */
+ readonly frameCopyAvailable = input(false);
+ /** Frame-copy engine is what the current app run actually uses */
+ readonly frameCopyActive = input(false);
readonly supportsManagedExternalPlayers = input(false);
readonly supportsExternalPlayerPathSettings = input(false);
readonly selectRecordingFolder = output();
diff --git a/apps/web/src/app/settings/settings.component.html b/apps/web/src/app/settings/settings.component.html
index 68aa05b07..c1bbd7758 100644
--- a/apps/web/src/app/settings/settings.component.html
+++ b/apps/web/src/app/settings/settings.component.html
@@ -50,6 +50,8 @@
[isDesktop]="isDesktop"
[supportsManagedExternalPlayers]="supportsManagedExternalPlayers"
[supportsExternalPlayerPathSettings]="supportsExternalPlayerPathSettings"
+ [frameCopyAvailable]="frameCopyAvailable()"
+ [frameCopyActive]="frameCopyActive()"
(selectRecordingFolder)="selectRecordingFolder()"
/>
diff --git a/apps/web/src/app/settings/settings.component.spec.ts b/apps/web/src/app/settings/settings.component.spec.ts
index 245985f79..95ffca533 100644
--- a/apps/web/src/app/settings/settings.component.spec.ts
+++ b/apps/web/src/app/settings/settings.component.spec.ts
@@ -102,6 +102,7 @@ const DEFAULT_SETTINGS = {
remoteControlPort: 8765,
epgUrl: [],
recordingFolder: '',
+ embeddedMpvFrameCopy: false,
coverSize: 'medium',
dashboardRails: DEFAULT_DASHBOARD_RAILS,
preferUploadedEpgOverXtream: false,
diff --git a/apps/web/src/app/settings/settings.component.ts b/apps/web/src/app/settings/settings.component.ts
index 66736cdfc..2b67bf278 100644
--- a/apps/web/src/app/settings/settings.component.ts
+++ b/apps/web/src/app/settings/settings.component.ts
@@ -165,6 +165,12 @@ export class SettingsComponent implements OnInit, OnDestroy {
readonly supportsEmbeddedMpv = computed(
() => this.isDesktop && !!this.embeddedMpvSupport()?.supported
);
+ readonly frameCopyAvailable = computed(
+ () => this.isDesktop && !!this.embeddedMpvSupport()?.frameCopyAvailable
+ );
+ readonly frameCopyActive = computed(
+ () => this.embeddedMpvSupport()?.engine === 'frame-copy'
+ );
readonly isPwa = this.runtime.isPwa;
diff --git a/apps/web/src/assets/i18n/ar.json b/apps/web/src/assets/i18n/ar.json
index 4293e33aa..7e63ec8f6 100644
--- a/apps/web/src/assets/i18n/ar.json
+++ b/apps/web/src/assets/i18n/ar.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "MPV المدمج: محرك نسخ الإطارات (تجريبي)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "يُرسم الفيديو على لوحة التطبيق بحيث تظهر القوائم ومربعات الحوار فوقه. متاح فقط على macOS بمعالج Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "يسري المفعول بعد إعادة تشغيل التطبيق."
},
"THEMES": {
"DARK_THEME": "داكن",
diff --git a/apps/web/src/assets/i18n/ary.json b/apps/web/src/assets/i18n/ary.json
index 4aad181c8..057077bdb 100644
--- a/apps/web/src/assets/i18n/ary.json
+++ b/apps/web/src/assets/i18n/ary.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "MPV المدمج: محرك نسخ الإطارات (تجريبي)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "الفيديو كيترسم على لوحة التطبيق باش القوائم والحوارات يبانو فوقو. غير على macOS ب Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "غادي يخدم من بعد ما تعاود تشغل التطبيق."
},
"THEMES": {
"DARK_THEME": "داكن",
diff --git a/apps/web/src/assets/i18n/by.json b/apps/web/src/assets/i18n/by.json
index 1e92b244f..20c5a7dc5 100644
--- a/apps/web/src/assets/i18n/by.json
+++ b/apps/web/src/assets/i18n/by.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: рухавік frame-copy (эксперыментальна)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Відэа малюецца на канвасе праграмы, таму меню і дыялогі паказваюцца па-над ім. Толькі macOS на Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Пачне дзейнічаць пасля перазапуску праграмы."
},
"THEMES": {
"DARK_THEME": "Цёмная",
diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json
index 4bb53182e..b5ba7e1e0 100644
--- a/apps/web/src/assets/i18n/de.json
+++ b/apps/web/src/assets/i18n/de.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: Frame-Copy-Engine (experimentell)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Das Video wird auf die App-Leinwand gerendert, sodass Menüs und Dialoge darüber liegen. Nur macOS auf Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Wird nach einem Neustart der App wirksam."
},
"THEMES": {
"DARK_THEME": "Dunkel",
diff --git a/apps/web/src/assets/i18n/el.json b/apps/web/src/assets/i18n/el.json
index 70b2795e3..b43040df2 100644
--- a/apps/web/src/assets/i18n/el.json
+++ b/apps/web/src/assets/i18n/el.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: μηχανή frame-copy (πειραματικό)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Το βίντεο σχεδιάζεται στον καμβά της εφαρμογής, ώστε τα μενού και τα παράθυρα διαλόγου να εμφανίζονται από πάνω του. Μόνο macOS με Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Ισχύει μετά την επανεκκίνηση της εφαρμογής."
},
"THEMES": {
"DARK_THEME": "Σκούρο",
diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json
index b77a49418..574359965 100644
--- a/apps/web/src/assets/i18n/en.json
+++ b/apps/web/src/assets/i18n/en.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: frame-copy engine (experimental)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Render video onto the app canvas so menus and dialogs stack above it. macOS Apple Silicon only.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Takes effect after restarting the app."
},
"THEMES": {
"DARK_THEME": "Dark",
diff --git a/apps/web/src/assets/i18n/es.json b/apps/web/src/assets/i18n/es.json
index 0c4087ff0..dc0f79392 100644
--- a/apps/web/src/assets/i18n/es.json
+++ b/apps/web/src/assets/i18n/es.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: motor frame-copy (experimental)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "El vídeo se dibuja en el lienzo de la aplicación, de modo que los menús y diálogos quedan encima. Solo macOS con Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Se aplica tras reiniciar la aplicación."
},
"THEMES": {
"DARK_THEME": "Oscuro",
diff --git a/apps/web/src/assets/i18n/fr.json b/apps/web/src/assets/i18n/fr.json
index 30be779d4..528294c42 100644
--- a/apps/web/src/assets/i18n/fr.json
+++ b/apps/web/src/assets/i18n/fr.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV : moteur frame-copy (expérimental)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "La vidéo est rendue sur le canevas de l'application, les menus et boîtes de dialogue s'affichent donc au-dessus. macOS Apple Silicon uniquement.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Prend effet après le redémarrage de l'application."
},
"THEMES": {
"DARK_THEME": "Sombre",
diff --git a/apps/web/src/assets/i18n/it.json b/apps/web/src/assets/i18n/it.json
index e5f8559a4..e4aeaa1ea 100644
--- a/apps/web/src/assets/i18n/it.json
+++ b/apps/web/src/assets/i18n/it.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: motore frame-copy (sperimentale)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Il video viene disegnato sul canvas dell'app, così menu e finestre di dialogo appaiono sopra di esso. Solo macOS con Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Ha effetto dopo il riavvio dell'app."
},
"THEMES": {
"DARK_THEME": "Scuro",
diff --git a/apps/web/src/assets/i18n/ja.json b/apps/web/src/assets/i18n/ja.json
index 9be5c7845..ef693252c 100644
--- a/apps/web/src/assets/i18n/ja.json
+++ b/apps/web/src/assets/i18n/ja.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "埋め込みMPV:フレームコピーエンジン(実験的)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "映像をアプリのキャンバスに描画するため、メニューやダイアログをその上に表示できます。Apple Silicon搭載macOSのみ。",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "アプリの再起動後に有効になります。"
},
"THEMES": {
"DARK_THEME": "ダーク",
diff --git a/apps/web/src/assets/i18n/ko.json b/apps/web/src/assets/i18n/ko.json
index 5b35c4905..082517e84 100644
--- a/apps/web/src/assets/i18n/ko.json
+++ b/apps/web/src/assets/i18n/ko.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "내장 MPV: 프레임 복사 엔진(실험적)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "비디오를 앱 캔버스에 렌더링하여 메뉴와 대화 상자가 그 위에 표시됩니다. Apple Silicon macOS 전용.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "앱을 다시 시작한 후 적용됩니다."
},
"THEMES": {
"DARK_THEME": "어둡게",
diff --git a/apps/web/src/assets/i18n/nl.json b/apps/web/src/assets/i18n/nl.json
index 70e472cce..d92459595 100644
--- a/apps/web/src/assets/i18n/nl.json
+++ b/apps/web/src/assets/i18n/nl.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: frame-copy-engine (experimenteel)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "De video wordt op het app-canvas getekend zodat menu's en dialoogvensters erboven verschijnen. Alleen macOS op Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Wordt van kracht na het herstarten van de app."
},
"THEMES": {
"DARK_THEME": "Donker",
diff --git a/apps/web/src/assets/i18n/pl.json b/apps/web/src/assets/i18n/pl.json
index 38fdaea82..74642284f 100644
--- a/apps/web/src/assets/i18n/pl.json
+++ b/apps/web/src/assets/i18n/pl.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: silnik frame-copy (eksperymentalny)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Wideo jest rysowane na kanwie aplikacji, dzięki czemu menu i okna dialogowe wyświetlają się nad nim. Tylko macOS na Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Zacznie działać po ponownym uruchomieniu aplikacji."
},
"THEMES": {
"DARK_THEME": "Ciemny",
diff --git a/apps/web/src/assets/i18n/pt.json b/apps/web/src/assets/i18n/pt.json
index a1b75be66..047f5af0b 100644
--- a/apps/web/src/assets/i18n/pt.json
+++ b/apps/web/src/assets/i18n/pt.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: motor frame-copy (experimental)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "O vídeo é desenhado na tela da aplicação, para que menus e diálogos fiquem por cima. Apenas macOS com Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Entra em vigor após reiniciar a aplicação."
},
"THEMES": {
"DARK_THEME": "Escuro",
diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json
index f043a6b13..f697e0a06 100644
--- a/apps/web/src/assets/i18n/ru.json
+++ b/apps/web/src/assets/i18n/ru.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Обновить",
"APP_UPDATE_INSTALL": "Перезагрузить приложение",
"APP_UPDATE_OPEN_RELEASE": "Открыть GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "Что нового"
+ "APP_UPDATE_RELEASE_NOTES": "Что нового",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: движок frame-copy (экспериментально)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Видео рисуется на канвасе приложения, поэтому меню и диалоги отображаются поверх него. Только macOS на Apple Silicon.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Вступит в силу после перезапуска приложения."
},
"THEMES": {
"DARK_THEME": "Тёмная",
diff --git a/apps/web/src/assets/i18n/tr.json b/apps/web/src/assets/i18n/tr.json
index 577f4ac60..0a6fceecc 100644
--- a/apps/web/src/assets/i18n/tr.json
+++ b/apps/web/src/assets/i18n/tr.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: frame-copy motoru (deneysel)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Video uygulama tuvaline çizilir, böylece menüler ve iletişim kutuları üzerinde görüntülenir. Yalnızca Apple Silicon macOS.",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "Uygulama yeniden başlatıldıktan sonra geçerli olur."
},
"THEMES": {
"DARK_THEME": "Koyu",
diff --git a/apps/web/src/assets/i18n/zh.json b/apps/web/src/assets/i18n/zh.json
index e473b0886..f35f3e5bf 100644
--- a/apps/web/src/assets/i18n/zh.json
+++ b/apps/web/src/assets/i18n/zh.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "嵌入式 MPV:帧拷贝引擎(实验性)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "视频渲染到应用画布上,菜单和对话框可以显示在其上方。仅限 Apple Silicon 的 macOS。",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "重启应用后生效。"
},
"THEMES": {
"DARK_THEME": "深色",
diff --git a/apps/web/src/assets/i18n/zhtw.json b/apps/web/src/assets/i18n/zhtw.json
index b138661e7..0988233f6 100644
--- a/apps/web/src/assets/i18n/zhtw.json
+++ b/apps/web/src/assets/i18n/zhtw.json
@@ -428,7 +428,10 @@
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
- "APP_UPDATE_RELEASE_NOTES": "What's new"
+ "APP_UPDATE_RELEASE_NOTES": "What's new",
+ "EMBEDDED_MPV_FRAME_COPY": "內嵌 MPV:幀複製引擎(實驗性)",
+ "EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "影片會繪製到應用程式畫布上,選單和對話框可顯示在其上方。僅限 Apple Silicon 的 macOS。",
+ "EMBEDDED_MPV_FRAME_COPY_RESTART": "重新啟動應用程式後生效。"
},
"THEMES": {
"DARK_THEME": "深色",
diff --git a/libs/services/src/lib/settings-store.service.ts b/libs/services/src/lib/settings-store.service.ts
index 883a5e8f8..ced7df83d 100644
--- a/libs/services/src/lib/settings-store.service.ts
+++ b/libs/services/src/lib/settings-store.service.ts
@@ -45,6 +45,7 @@ const DEFAULT_SETTINGS: Settings = {
epgUrl: [],
downloadFolder: '',
recordingFolder: '',
+ embeddedMpvFrameCopy: false,
coverSize: 'medium',
epgViewMode: 'timeline',
dashboardRails: DEFAULT_DASHBOARD_RAILS_SETTINGS,
@@ -187,6 +188,8 @@ export const SettingsStore = signalStore(
recordingFolder:
store.recordingFolder?.() ??
DEFAULT_SETTINGS.recordingFolder,
+ embeddedMpvFrameCopy:
+ store.embeddedMpvFrameCopy?.() ?? false,
coverSize: store.coverSize?.() ?? DEFAULT_SETTINGS.coverSize,
epgViewMode:
store.epgViewMode?.() ?? DEFAULT_SETTINGS.epgViewMode,
diff --git a/libs/shared/interfaces/src/lib/embedded-mpv-session.interface.ts b/libs/shared/interfaces/src/lib/embedded-mpv-session.interface.ts
index 4d1b696da..0a02540bd 100644
--- a/libs/shared/interfaces/src/lib/embedded-mpv-session.interface.ts
+++ b/libs/shared/interfaces/src/lib/embedded-mpv-session.interface.ts
@@ -35,6 +35,12 @@ export interface EmbeddedMpvSupport {
* `frame-copy` = helper process + shm ring + renderer canvas.
*/
engine?: EmbeddedMpvEngine;
+ /**
+ * True when this machine could run the frame-copy engine (macOS arm64
+ * with the helper binary present), regardless of whether it is active.
+ * Drives the Settings toggle; switching engines requires an app restart.
+ */
+ frameCopyAvailable?: boolean;
}
/**
diff --git a/libs/shared/interfaces/src/lib/settings.interface.ts b/libs/shared/interfaces/src/lib/settings.interface.ts
index 0242ca73a..6c394e19a 100644
--- a/libs/shared/interfaces/src/lib/settings.interface.ts
+++ b/libs/shared/interfaces/src/lib/settings.interface.ts
@@ -108,6 +108,12 @@ export interface Settings {
downloadFolder?: string;
/** Custom live recording folder path (uses system Downloads folder if not set) */
recordingFolder?: string;
+ /**
+ * Embedded MPV frame-copy engine (experimental, macOS Apple Silicon).
+ * Applied on the next app start — the engine relaxes the window sandbox
+ * for its preload frame pump, which is fixed at window creation.
+ */
+ embeddedMpvFrameCopy?: boolean;
/** Cover/poster sizing preset applied across grids and rails */
coverSize?: CoverSize;
/** Live EPG panel layout: horizontal timeline (default) or vertical list */