mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
* fix(playback): harden embedded mpv session handling and support detection - guard the session controller against late startup rejections clobbering a newer session during fast channel zapping - exclude the refresh timestamp from the session-update dedup key so idle sessions stop re-emitting IPC updates every 500 ms - macOS: reconcile async loadfile replies by request id so a rejected seek/aid/speed on a live stream no longer flips the session to error - append --ozone-platform=x11 on Linux in main.ts so direct binary and AppImage launches match the packaged .desktop launcher behavior - return a sandbox-specific unsupported reason in Flatpak/Snap instead of asking the user to install mpv inside the sandbox - update the stale "macOS only" embedded MPV claim in CLAUDE.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(playback): populate Linux audio tracks and fix ARM Linux packaging - Linux poller now reads track-list/count each tick and walks the scalar track-list/N/* sub-properties when the count changes, so the audio-track menu is no longer empty; selection reconciles from the aid property - afterPack replaces the x64 embedded_mpv.node with an embedded-mpv-unavailable.txt marker in arm64/armv7l Linux packages, and package-layout verification rejects foreign-architecture addons while requiring the marker - extend native source invariants for the non-fatal async-reply rule (macOS) and the Linux track-list polling contract - document the Linux track-list mechanics and ARM packaging behavior in docs/architecture/embedded-mpv-native.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(playback): embedded mpv player UX polish and full localization - click on the video toggles pause (same action as Space) with a 250 ms grace period so double-click fullscreen cancels the pending pause; no DOM overlay is drawn — the dock transport icon is the feedback - timeline scrubbing previews the drag position locally and commits a single seek on release instead of one IPC seek per drag pixel - translate all player UI strings (controls, tooltips, aria-labels, status and recording messages) via new EMBEDDED_MPV.PLAYER.* keys, synced across en + 17 locales through the i18n-fill workflow - replace the legacy @Output() EventEmitter with the signal output() API Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): react to language changes and respect ozone platform hint Address review feedback on #1122: - add a translationsTick signal (onLangChange/onTranslationChange/ onDefaultLangChange) read by every computed() and template helper that calls translate.instant(), so labels re-evaluate on a runtime language switch and when the translation file finishes loading after mount - suppress the Linux --ozone-platform=x11 fallback when the user set ELECTRON_OZONE_PLATFORM_HINT, matching the existing respect for an explicit --ozone-platform switch Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
133 lines
3.7 KiB
JavaScript
133 lines
3.7 KiB
JavaScript
const linuxAfterPack = require('./linux-after-pack.cjs');
|
|
const {
|
|
isForeignLinuxEmbeddedMpvArch,
|
|
resolveElectronBuilderArchName,
|
|
validatePackagedEmbeddedMpv,
|
|
} = require('./embedded-mpv-packaging.cjs');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
function log(message) {
|
|
console.log(` - ${message}`);
|
|
}
|
|
|
|
function isTruthy(value) {
|
|
return ['1', 'true', 'yes', 'on'].includes(
|
|
String(value ?? '')
|
|
.trim()
|
|
.toLowerCase()
|
|
);
|
|
}
|
|
|
|
function copyEmbeddedMpvNativeOutput(resourceDir, projectDir) {
|
|
const sourceDir = path.join(
|
|
projectDir,
|
|
'dist',
|
|
'apps',
|
|
'electron-backend',
|
|
'native'
|
|
);
|
|
|
|
if (!fs.existsSync(sourceDir)) {
|
|
return;
|
|
}
|
|
|
|
const destinationDir = path.join(
|
|
resourceDir,
|
|
'app.asar.unpacked',
|
|
'electron-backend',
|
|
'native'
|
|
);
|
|
|
|
fs.rmSync(destinationDir, { recursive: true, force: true });
|
|
fs.cpSync(sourceDir, destinationDir, { recursive: true });
|
|
}
|
|
|
|
function writeEmbeddedMpvUnavailableMarker(resourceDir, targetArch) {
|
|
const destinationDir = path.join(
|
|
resourceDir,
|
|
'app.asar.unpacked',
|
|
'electron-backend',
|
|
'native'
|
|
);
|
|
|
|
fs.rmSync(destinationDir, { recursive: true, force: true });
|
|
fs.mkdirSync(destinationDir, { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(destinationDir, 'embedded-mpv-unavailable.txt'),
|
|
`Embedded MPV is not bundled for ${targetArch} Linux builds yet. The built-in player and external MPV/VLC remain available.\n`
|
|
);
|
|
}
|
|
|
|
async function afterPackHook(params) {
|
|
await linuxAfterPack(params);
|
|
|
|
const requireEmbeddedMpv = isTruthy(
|
|
process.env.IPTVNATOR_REQUIRE_EMBEDDED_MPV
|
|
);
|
|
log(
|
|
requireEmbeddedMpv
|
|
? `validating required embedded MPV ${params.electronPlatformName} runtime`
|
|
: `validating optional embedded MPV ${params.electronPlatformName} runtime`
|
|
);
|
|
const resourceDir = getResourceDir(params);
|
|
|
|
const foreignArch = isForeignLinuxEmbeddedMpvArch(
|
|
params.electronPlatformName,
|
|
params.arch
|
|
);
|
|
if (foreignArch) {
|
|
const targetArch = resolveElectronBuilderArchName(params.arch);
|
|
log(
|
|
`embedded MPV addon is not built for ${targetArch}; packaging an unavailable marker instead`
|
|
);
|
|
writeEmbeddedMpvUnavailableMarker(resourceDir, targetArch);
|
|
} else {
|
|
copyEmbeddedMpvNativeOutput(
|
|
resourceDir,
|
|
params.packager.projectDir ?? process.cwd()
|
|
);
|
|
}
|
|
|
|
const errors = validatePackagedEmbeddedMpv(resourceDir, {
|
|
platform: params.electronPlatformName,
|
|
required: requireEmbeddedMpv,
|
|
foreignArch,
|
|
});
|
|
|
|
if (errors.length > 0) {
|
|
throw new Error(
|
|
[
|
|
`Embedded MPV ${params.electronPlatformName} package validation failed.`,
|
|
...errors.map((error) => `- ${error}`),
|
|
].join('\n')
|
|
);
|
|
}
|
|
|
|
log(`embedded MPV ${params.electronPlatformName} runtime validated`);
|
|
}
|
|
|
|
function getResourceDir(params) {
|
|
if (params.electronPlatformName !== 'darwin') {
|
|
return path.join(params.appOutDir, 'resources');
|
|
}
|
|
|
|
const appPath = params.appOutDir.endsWith('.app')
|
|
? params.appOutDir
|
|
: fs
|
|
.readdirSync(params.appOutDir)
|
|
.find((entry) => entry.endsWith('.app'));
|
|
|
|
return appPath
|
|
? path.join(
|
|
params.appOutDir.endsWith('.app')
|
|
? params.appOutDir
|
|
: path.join(params.appOutDir, appPath),
|
|
'Contents',
|
|
'Resources'
|
|
)
|
|
: params.appOutDir;
|
|
}
|
|
|
|
module.exports = afterPackHook;
|