mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
feat(embedded-mpv): Settings toggle for the frame-copy engine
New 'Embedded MPV: frame-copy engine' checkbox in Settings > Playback, shown only when the machine can run it (macOS arm64 with the helper binary present — support now reports frameCopyAvailable). The choice persists to the main-process config store because the engine relaxes the window sandbox for the preload frame pump, which is fixed at window creation: main.ts reads the store before creating the window and sets the engine env var; an explicitly set env var (including '0') always wins, and the UI shows a restart hint while the saved choice differs from the active engine. Localized in all 18 locales. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5a3ea5a3c1
commit
d39f5f848a
31 files changed
+182
-18
No files matched your search
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -114,6 +114,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (frameCopyAvailable()) {
|
||||
<div
|
||||
class="setting-item"
|
||||
data-test-id="embedded-mpv-frame-copy-setting"
|
||||
>
|
||||
<div class="setting-item__meta">
|
||||
<h4>
|
||||
{{ 'SETTINGS.EMBEDDED_MPV_FRAME_COPY' | translate }}
|
||||
</h4>
|
||||
<p>
|
||||
{{
|
||||
'SETTINGS.EMBEDDED_MPV_FRAME_COPY_DESCRIPTION'
|
||||
| translate
|
||||
}}
|
||||
@if (
|
||||
form().value.embeddedMpvFrameCopy !==
|
||||
frameCopyActive()
|
||||
) {
|
||||
<strong>
|
||||
{{
|
||||
'SETTINGS.EMBEDDED_MPV_FRAME_COPY_RESTART'
|
||||
| translate
|
||||
}}
|
||||
</strong>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div class="setting-item__control setting-item__toggle">
|
||||
<mat-checkbox
|
||||
formControlName="embeddedMpvFrameCopy"
|
||||
data-test-id="embedded-mpv-frame-copy-toggle"
|
||||
></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="setting-item" data-test-id="recording-folder-setting">
|
||||
<div class="setting-item__meta">
|
||||
<h4>{{ 'SETTINGS.RECORDING_FOLDER_LABEL' | translate }}</h4>
|
||||
|
||||
@@ -45,6 +45,10 @@ export class SettingsPlaybackSectionComponent {
|
||||
readonly players = input.required<SettingsPlayerOption[]>();
|
||||
readonly streamFormatEnum = input.required<typeof StreamFormat>();
|
||||
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<void>();
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
[isDesktop]="isDesktop"
|
||||
[supportsManagedExternalPlayers]="supportsManagedExternalPlayers"
|
||||
[supportsExternalPlayerPathSettings]="supportsExternalPlayerPathSettings"
|
||||
[frameCopyAvailable]="frameCopyAvailable()"
|
||||
[frameCopyActive]="frameCopyActive()"
|
||||
(selectRecordingFolder)="selectRecordingFolder()"
|
||||
/>
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ const DEFAULT_SETTINGS = {
|
||||
remoteControlPort: 8765,
|
||||
epgUrl: [],
|
||||
recordingFolder: '',
|
||||
embeddedMpvFrameCopy: false,
|
||||
coverSize: 'medium',
|
||||
dashboardRails: DEFAULT_DASHBOARD_RAILS,
|
||||
preferUploadedEpgOverXtream: false,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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": "داكن",
|
||||
|
||||
@@ -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": "داكن",
|
||||
|
||||
@@ -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": "Цёмная",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "Σκούρο",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "ダーク",
|
||||
|
||||
@@ -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": "어둡게",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "Тёмная",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "深色",
|
||||
|
||||
@@ -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": "深色",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in new issue
Block a user