diff --git a/apps/electron-backend/src/app/events/player.events.spec.ts b/apps/electron-backend/src/app/events/player.events.spec.ts index d8d2e71b4..e04241c92 100644 --- a/apps/electron-backend/src/app/events/player.events.spec.ts +++ b/apps/electron-backend/src/app/events/player.events.spec.ts @@ -35,8 +35,10 @@ import { } from '../services/store.service'; import { buildExternalPlayerSpawnSpec, + buildPlayerArgsWithCustomArguments, buildVlcEnqueueCommands, isRunningInFlatpak, + parseExternalPlayerArguments, parseVlcRcPlaybackState, parseVlcRcNumericResponse, resolveExternalPlayerLaunchContext, @@ -67,21 +69,25 @@ function getIpcMainHandler(channel: string): (...args: unknown[]) => unknown { describe('player.events Flatpak launch helpers', () => { it('detects Flatpak only on Linux when /.flatpak-info exists', () => { - expect(isRunningInFlatpak(createPathExists(['/.flatpak-info']), 'linux')).toBe( - true - ); - expect(isRunningInFlatpak(createPathExists(['/.flatpak-info']), 'darwin')).toBe( - false - ); + expect( + isRunningInFlatpak(createPathExists(['/.flatpak-info']), 'linux') + ).toBe(true); + expect( + isRunningInFlatpak(createPathExists(['/.flatpak-info']), 'darwin') + ).toBe(false); expect(isRunningInFlatpak(createPathExists([]), 'linux')).toBe(false); }); it('keeps direct Linux player launching outside Flatpak', () => { - const launchContext = resolveExternalPlayerLaunchContext('mpv', undefined, { - platform: 'linux', - isFlatpak: false, - pathExists: createPathExists(['/usr/local/bin/mpv']), - }); + const launchContext = resolveExternalPlayerLaunchContext( + 'mpv', + undefined, + { + platform: 'linux', + isFlatpak: false, + pathExists: createPathExists(['/usr/local/bin/mpv']), + } + ); const spawnSpec = buildExternalPlayerSpawnSpec(launchContext, [ '--ytdl=no', 'https://example.com/stream.m3u8', @@ -102,11 +108,15 @@ describe('player.events Flatpak launch helpers', () => { }); it('builds Flatpak host launches with bare player names by default', () => { - const launchContext = resolveExternalPlayerLaunchContext('vlc', undefined, { - platform: 'linux', - isFlatpak: true, - pathExists: createPathExists(['/usr/bin/vlc']), - }); + const launchContext = resolveExternalPlayerLaunchContext( + 'vlc', + undefined, + { + platform: 'linux', + isFlatpak: true, + pathExists: createPathExists(['/usr/bin/vlc']), + } + ); const spawnSpec = buildExternalPlayerSpawnSpec(launchContext, [ '--extraintf=rc', 'https://example.com/stream.m3u8', @@ -199,6 +209,38 @@ describe('player.events Flatpak launch helpers', () => { }); }); + it('parses custom player arguments as one argument per non-empty line', () => { + expect( + parseExternalPlayerArguments( + ' --screen=1\n\n--geometry=1280x720\r\n --hwdec=auto-safe ' + ) + ).toEqual(['--screen=1', '--geometry=1280x720', '--hwdec=auto-safe']); + }); + + it('treats missing custom player arguments as no arguments', () => { + expect(parseExternalPlayerArguments(undefined)).toEqual([]); + expect(parseExternalPlayerArguments(' \n ')).toEqual([]); + }); + + it('adds custom player arguments before IPTVnator runtime arguments', () => { + expect( + buildPlayerArgsWithCustomArguments( + '--screen=1\n--geometry=1280x720', + [ + '--ytdl=no', + '--force-media-title=News', + 'https://example.com/stream.m3u8', + ] + ) + ).toEqual([ + '--screen=1', + '--geometry=1280x720', + '--ytdl=no', + '--force-media-title=News', + 'https://example.com/stream.m3u8', + ]); + }); + it('disables MPV reuse and socket bridging only in Flatpak', () => { expect(shouldReuseMpvInstance(true, true)).toBe(false); expect(shouldUseMpvSocketBridge(true)).toBe(false); @@ -315,9 +357,7 @@ describe('buildVlcEnqueueCommands', () => { origin: 'https://origin.example', }); - expect(commands[1]).toContain( - ':http-referrer=https://origin.example' - ); + expect(commands[1]).toContain(':http-referrer=https://origin.example'); }); it('appends a seek command when startTime is provided', () => { diff --git a/apps/electron-backend/src/app/events/player.events.ts b/apps/electron-backend/src/app/events/player.events.ts index 93c7522d6..3b3e66838 100644 --- a/apps/electron-backend/src/app/events/player.events.ts +++ b/apps/electron-backend/src/app/events/player.events.ts @@ -4,12 +4,16 @@ import { EXTERNAL_PLAYER_SESSION_UPDATE, ExternalPlayerName, ExternalPlayerSession, + parseExternalPlayerArguments, + type ExternalPlayerArgumentsInput, } from 'shared-interfaces'; import App from '../app'; import { + MPV_PLAYER_ARGUMENTS, MPV_PLAYER_PATH, MPV_REUSE_INSTANCE, store, + VLC_PLAYER_ARGUMENTS, VLC_PLAYER_PATH, VLC_REUSE_INSTANCE, } from '../services/store.service'; @@ -43,7 +47,10 @@ interface ExternalPlaybackSnapshot { function sendExternalPlayerSessionUpdate(session: ExternalPlayerSession) { if (App.mainWindow && !App.mainWindow.isDestroyed()) { - App.mainWindow.webContents.send(EXTERNAL_PLAYER_SESSION_UPDATE, session); + App.mainWindow.webContents.send( + EXTERNAL_PLAYER_SESSION_UPDATE, + session + ); } } @@ -202,6 +209,15 @@ export function buildExternalPlayerSpawnSpec( }; } +export { parseExternalPlayerArguments }; + +export function buildPlayerArgsWithCustomArguments( + customArguments: ExternalPlayerArgumentsInput, + playerArgs: string[] +): string[] { + return [...parseExternalPlayerArguments(customArguments), ...playerArgs]; +} + export function shouldReuseMpvInstance( requestedReuseInstance: boolean, isFlatpak: boolean = isRunningInFlatpak() @@ -504,8 +520,10 @@ function isStalkerDirectStreamProfile( const icyMetaData = headers['Icy-MetaData'] ?? headers['icy-metadata']; const userAgent = headers['User-Agent'] ?? headers['user-agent']; - return String(icyMetaData).trim() === '1' && - String(userAgent).trim().toLowerCase() === 'ksplayer'; + return ( + String(icyMetaData).trim() === '1' && + String(userAgent).trim().toLowerCase() === 'ksplayer' + ); } function startPositionPolling( @@ -592,9 +610,7 @@ async function getVlcProperty(port: number, command: string): Promise { } async function getVlcPlaybackState(port: number): Promise { - return parseVlcRcPlaybackState( - await getVlcCommandResponse(port, 'status') - ); + return parseVlcRcPlaybackState(await getVlcCommandResponse(port, 'status')); } async function getVlcPlaybackSnapshot( @@ -661,7 +677,10 @@ function startVlcPositionPolling( } // Helper function to send command to MPV via IPC -function sendMpvCommand(command: string, args: Array): Promise { +function sendMpvCommand( + command: string, + args: Array +): Promise { return new Promise((resolve, reject) => { if (!mpvSocketPath) { reject(new Error('No MPV socket path available')); @@ -715,6 +734,7 @@ ipcMain.handle( getMpvPath({ isFlatpak }), { isFlatpak } ); + const customMpvArguments = store.get(MPV_PLAYER_ARGUMENTS, ''); const requestedReuseInstance = store.get(MPV_REUSE_INSTANCE, false); const reuseInstance = shouldReuseMpvInstance( requestedReuseInstance, @@ -760,6 +780,8 @@ ipcMain.handle( headerCount: headerFields.length, hasContentInfo: Boolean(contentInfo), startTime: startTime ?? null, + customArgumentCount: + parseExternalPlayerArguments(customMpvArguments).length, }); // If reuse is enabled and there's an existing process, try to use it @@ -810,15 +832,18 @@ ipcMain.handle( 'Successfully loaded new URL in existing MPV instance' ); - externalPlayerSessions.attachCloser(session.id, async () => { - try { - await sendMpvCommand('quit', []); - } catch { - if (mpvProcess && !mpvProcess.killed) { - mpvProcess.kill(); + externalPlayerSessions.attachCloser( + session.id, + async () => { + try { + await sendMpvCommand('quit', []); + } catch { + if (mpvProcess && !mpvProcess.killed) { + mpvProcess.kill(); + } } } - }); + ); // Seek if startTime provided if (startTime) { @@ -904,7 +929,7 @@ ipcMain.handle( await new Promise((resolve, reject) => { const spawnSpec = buildExternalPlayerSpawnSpec( mpvLaunchContext, - args + buildPlayerArgsWithCustomArguments(customMpvArguments, args) ); const proc = spawn(spawnSpec.command, spawnSpec.args, { shell: false, @@ -1114,10 +1139,8 @@ ipcMain.handle( getVlcPath({ isFlatpak }), { isFlatpak } ); - const requestedReuseInstance = store.get( - VLC_REUSE_INSTANCE, - false - ); + const customVlcArguments = store.get(VLC_PLAYER_ARGUMENTS, ''); + const requestedReuseInstance = store.get(VLC_REUSE_INSTANCE, false); const reuseInstance = shouldReuseVlcInstance( requestedReuseInstance, isFlatpak @@ -1155,6 +1178,8 @@ ipcMain.handle( hasOrigin: Boolean(effectiveOrigin), hasContentInfo: Boolean(contentInfo), startTime: startTime ?? null, + customArgumentCount: + parseExternalPlayerArguments(customVlcArguments).length, }); // Try to drive an already-running VLC via its RC port instead of @@ -1316,7 +1341,10 @@ ipcMain.handle( const spawnVlc = (playerArgs: string[], isRetry = false) => { const spawnSpec = buildExternalPlayerSpawnSpec( vlcLaunchContext, - playerArgs + buildPlayerArgsWithCustomArguments( + customVlcArguments, + playerArgs + ) ); const trackProcess = reuseInstance && !isRetry; const proc = spawn(spawnSpec.command, spawnSpec.args, { @@ -1338,8 +1366,8 @@ ipcMain.handle( const markVlcSessionClosed = () => { if ( - externalPlayerSessions.getSession(session.id)?.status === - 'closed' + externalPlayerSessions.getSession(session.id) + ?.status === 'closed' ) { return; } @@ -1375,27 +1403,30 @@ ipcMain.handle( ); }; - externalPlayerSessions.attachCloser(session.id, async () => { - await flushVlcPlaybackPosition(); - if (!proc.killed) { - proc.kill(); + externalPlayerSessions.attachCloser( + session.id, + async () => { + await flushVlcPlaybackPosition(); + if (!proc.killed) { + proc.kill(); + } } - }); + ); // Start polling if we have port and content info and NOT retrying (RC disabled on retry) - if (!isRetry && rcPort > 0 && contentInfo) { - startVlcPositionPolling( - rcPort, - contentInfo, - session.id, - (snapshot) => { - lastVlcSnapshot = snapshot; - }, - () => { - markVlcSessionClosed(); - } - ); - } + if (!isRetry && rcPort > 0 && contentInfo) { + startVlcPositionPolling( + rcPort, + contentInfo, + session.id, + (snapshot) => { + lastVlcSnapshot = snapshot; + }, + () => { + markVlcSessionClosed(); + } + ); + } // Capture stdout if (proc.stdout) { diff --git a/apps/electron-backend/src/app/events/settings.events.ts b/apps/electron-backend/src/app/events/settings.events.ts index 322d5720f..ee35552a2 100644 --- a/apps/electron-backend/src/app/events/settings.events.ts +++ b/apps/electron-backend/src/app/events/settings.events.ts @@ -1,7 +1,10 @@ import { ipcMain } from 'electron'; +import { normalizeExternalPlayerArguments } from 'shared-interfaces'; import { + MPV_PLAYER_ARGUMENTS, MPV_REUSE_INSTANCE, store, + VLC_PLAYER_ARGUMENTS, VLC_REUSE_INSTANCE, } from '../services/store.service'; import { httpServer } from '../server/http-server'; @@ -15,6 +18,20 @@ export default class SettingsEvents { ipcMain.handle('SETTINGS_UPDATE', (_event, arg) => { console.log('Received SETTINGS_UPDATE with data:', arg); + if (arg.mpvPlayerArguments !== undefined) { + store.set( + MPV_PLAYER_ARGUMENTS, + normalizeExternalPlayerArguments(arg.mpvPlayerArguments) + ); + } + + if (arg.vlcPlayerArguments !== undefined) { + store.set( + VLC_PLAYER_ARGUMENTS, + normalizeExternalPlayerArguments(arg.vlcPlayerArguments) + ); + } + // Only set values that are defined if (arg.mpvReuseInstance !== undefined) { store.set(MPV_REUSE_INSTANCE, arg.mpvReuseInstance); @@ -25,9 +42,13 @@ ipcMain.handle('SETTINGS_UPDATE', (_event, arg) => { } // Handle remote control settings - if (arg.remoteControl !== undefined || arg.remoteControlPort !== undefined) { + if ( + arg.remoteControl !== undefined || + arg.remoteControlPort !== undefined + ) { const enabled = arg.remoteControl ?? store.get('remoteControl', false); - const port = arg.remoteControlPort ?? store.get('remoteControlPort', 8765); + const port = + arg.remoteControlPort ?? store.get('remoteControlPort', 8765); // Save to store if (arg.remoteControl !== undefined) { diff --git a/apps/electron-backend/src/app/services/store.service.ts b/apps/electron-backend/src/app/services/store.service.ts index d83b3e607..d1e01ec23 100644 --- a/apps/electron-backend/src/app/services/store.service.ts +++ b/apps/electron-backend/src/app/services/store.service.ts @@ -3,14 +3,18 @@ import { getElectronConfigDirectory } from 'database'; export const WINDOW_BOUNDS = 'WINDOW_BOUNDS'; export const MPV_PLAYER_PATH = 'MPV_PLAYER_PATH'; +export const MPV_PLAYER_ARGUMENTS = 'MPV_PLAYER_ARGUMENTS'; 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'; export type StoreType = { [WINDOW_BOUNDS]: Electron.Rectangle; [MPV_PLAYER_PATH]: string; + [MPV_PLAYER_ARGUMENTS]: string; [VLC_PLAYER_PATH]: string; + [VLC_PLAYER_ARGUMENTS]: string; [MPV_REUSE_INSTANCE]: boolean; [VLC_REUSE_INSTANCE]: boolean; }; diff --git a/apps/web/src/app/app.component.spec.ts b/apps/web/src/app/app.component.spec.ts index 7b0d95088..1eca36f97 100644 --- a/apps/web/src/app/app.component.spec.ts +++ b/apps/web/src/app/app.component.spec.ts @@ -44,8 +44,10 @@ const DEFAULT_SETTINGS: Settings = { showExternalPlaybackBar: true, theme: Theme.SystemTheme, mpvPlayerPath: '', + mpvPlayerArguments: '', mpvReuseInstance: false, vlcPlayerPath: '', + vlcPlayerArguments: '', vlcReuseInstance: false, remoteControl: false, remoteControlPort: 8765, 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 76494e5e6..b3a26a9d7 100644 --- a/apps/web/src/app/settings/settings-playback-section.component.html +++ b/apps/web/src/app/settings/settings-playback-section.component.html @@ -174,6 +174,39 @@ +
+
+

{{ 'SETTINGS.MPV_PLAYER_ARGUMENTS_LABEL' | translate }}

+

+ {{ + 'SETTINGS.MPV_PLAYER_ARGUMENTS_DESCRIPTION' | translate + }} +

+
+
+ + {{ + 'SETTINGS.MPV_PLAYER_ARGUMENTS' | translate + }} + + {{ + 'SETTINGS.EXTERNAL_PLAYER_ARGUMENTS_HINT' | translate + }} + +
+
+

{{ 'SETTINGS.MPV_REUSE_INSTANCE_LABEL' | translate }}

@@ -210,6 +243,39 @@
+
+
+

{{ 'SETTINGS.VLC_PLAYER_ARGUMENTS_LABEL' | translate }}

+

+ {{ + 'SETTINGS.VLC_PLAYER_ARGUMENTS_DESCRIPTION' | translate + }} +

+
+
+ + {{ + 'SETTINGS.VLC_PLAYER_ARGUMENTS' | translate + }} + + {{ + 'SETTINGS.EXTERNAL_PLAYER_ARGUMENTS_HINT' | translate + }} + +
+
+

{{ 'SETTINGS.VLC_REUSE_INSTANCE_LABEL' | translate }}

diff --git a/apps/web/src/app/settings/settings-playback-section.component.spec.ts b/apps/web/src/app/settings/settings-playback-section.component.spec.ts index 77fefc620..e0c2ef768 100644 --- a/apps/web/src/app/settings/settings-playback-section.component.spec.ts +++ b/apps/web/src/app/settings/settings-playback-section.component.spec.ts @@ -11,6 +11,9 @@ const MPV_COMPATIBLE_PLAYER_TIP = 'IINA can be launched as an MPV-compatible player on macOS, but use its executable path, such as /Applications/IINA.app/Contents/MacOS/iina-cli or /Applications/IINA.app/Contents/MacOS/IINA. IPTVnator controls, position polling, and reuse-instance behavior are guaranteed only for MPV IPC.'; const VLC_PATH_DESCRIPTION = 'Set the path to VLC. On macOS you can use the VLC app bundle, such as /Applications/VLC.app, or the executable path.'; +const MPV_ARGUMENTS_PLACEHOLDER = + '--ontop\n--autofit=640x360\n--geometry=+80+80'; +const VLC_ARGUMENTS_PLACEHOLDER = '--video-on-top\n--width=640\n--height=360'; describe('SettingsPlaybackSectionComponent', () => { let fixture: ComponentFixture; @@ -226,6 +229,56 @@ describe('SettingsPlaybackSectionComponent', () => { ) ).toBeNull(); }); + + it('shows MPV command-line arguments only when MPV is selected', () => { + fixture.componentRef.setInput('form', createForm(VideoPlayer.MPV)); + fixture.componentRef.setInput('isDesktop', true); + fixture.detectChanges(); + + expect( + fixture.nativeElement.querySelector( + '[data-test-id="mpv-player-arguments-setting"]' + ) + ).not.toBeNull(); + expect( + fixture.nativeElement.querySelector( + '[data-test-id="vlc-player-arguments-setting"]' + ) + ).toBeNull(); + expect(fixture.nativeElement.textContent).toContain( + 'SETTINGS.MPV_PLAYER_ARGUMENTS_LABEL' + ); + expect( + fixture.nativeElement.querySelector( + '#mpvPlayerArguments' + )?.placeholder + ).toBe(MPV_ARGUMENTS_PLACEHOLDER); + }); + + it('shows VLC command-line arguments only when VLC is selected', () => { + fixture.componentRef.setInput('form', createForm(VideoPlayer.VLC)); + fixture.componentRef.setInput('isDesktop', true); + fixture.detectChanges(); + + expect( + fixture.nativeElement.querySelector( + '[data-test-id="vlc-player-arguments-setting"]' + ) + ).not.toBeNull(); + expect( + fixture.nativeElement.querySelector( + '[data-test-id="mpv-player-arguments-setting"]' + ) + ).toBeNull(); + expect(fixture.nativeElement.textContent).toContain( + 'SETTINGS.VLC_PLAYER_ARGUMENTS_LABEL' + ); + expect( + fixture.nativeElement.querySelector( + '#vlcPlayerArguments' + )?.placeholder + ).toBe(VLC_ARGUMENTS_PLACEHOLDER); + }); }); function createForm(player = VideoPlayer.VideoJs): FormGroup { @@ -235,8 +288,10 @@ function createForm(player = VideoPlayer.VideoJs): FormGroup { openStreamOnDoubleClick: new FormControl(false), showExternalPlaybackBar: new FormControl(true), mpvPlayerPath: new FormControl(''), + mpvPlayerArguments: new FormControl(''), mpvReuseInstance: new FormControl(false), vlcPlayerPath: new FormControl(''), + vlcPlayerArguments: new FormControl(''), vlcReuseInstance: new FormControl(false), recordingFolder: new FormControl(''), }); 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 52aa7b29a..6f869bd41 100644 --- a/apps/web/src/app/settings/settings-playback-section.component.ts +++ b/apps/web/src/app/settings/settings-playback-section.component.ts @@ -29,6 +29,17 @@ import { SettingsPlayerOption } from './settings.models'; styles: [':host { display: contents; }'], }) export class SettingsPlaybackSectionComponent { + readonly mpvPlayerArgumentsPlaceholder = [ + '--ontop', + '--autofit=640x360', + '--geometry=+80+80', + ].join('\n'); + readonly vlcPlayerArgumentsPlaceholder = [ + '--video-on-top', + '--width=640', + '--height=360', + ].join('\n'); + readonly form = input.required(); readonly activeSection = input.required(); readonly players = input.required(); diff --git a/apps/web/src/app/settings/settings.component.scss b/apps/web/src/app/settings/settings.component.scss index 18481594d..5fcd5c475 100644 --- a/apps/web/src/app/settings/settings.component.scss +++ b/apps/web/src/app/settings/settings.component.scss @@ -211,6 +211,30 @@ justify-content: stretch; } +.setting-item--external-player-arguments { + grid-template-columns: 1fr; + gap: 10px; + + .setting-item__control { + justify-content: stretch; + align-items: stretch; + } + + mat-form-field { + width: 100%; + } + + textarea { + min-height: 86px; + resize: vertical; + font-family: + 'JetBrains Mono', 'SFMono-Regular', Menlo, Monaco, Consolas, + monospace; + font-size: 0.84rem; + line-height: 1.45; + } +} + .theme-switcher { width: min(100%, 540px); display: grid; diff --git a/apps/web/src/app/settings/settings.component.spec.ts b/apps/web/src/app/settings/settings.component.spec.ts index 85e16e460..488c5f96d 100644 --- a/apps/web/src/app/settings/settings.component.spec.ts +++ b/apps/web/src/app/settings/settings.component.spec.ts @@ -76,8 +76,10 @@ const DEFAULT_SETTINGS = { showExternalPlaybackBar: true, theme: Theme.SystemTheme, mpvPlayerPath: '', + mpvPlayerArguments: '', mpvReuseInstance: false, vlcPlayerPath: '', + vlcPlayerArguments: '', vlcReuseInstance: false, remoteControl: false, remoteControlPort: 8765, @@ -953,4 +955,31 @@ describe('SettingsComponent', () => { expect(setMpvPlayerPath).toHaveBeenCalledWith(''); expect(setVlcPlayerPath).toHaveBeenCalledWith(''); }); + + it('saves external player command-line arguments with the settings payload', async () => { + const mockStore = settingsStore as unknown as MockSettingsStore; + mockStore.updateSettings.mockResolvedValue(undefined); + const updateSettings = jest.spyOn(window.electron, 'updateSettings'); + + component.settingsForm.patchValue({ + mpvPlayerArguments: '--screen=1\n--geometry=1280x720', + vlcPlayerArguments: '--qt-fullscreen-screennumber=1', + }); + + component.onSubmit(); + await fixture.whenStable(); + + expect(mockStore.updateSettings).toHaveBeenCalledWith( + expect.objectContaining({ + mpvPlayerArguments: '--screen=1\n--geometry=1280x720', + vlcPlayerArguments: '--qt-fullscreen-screennumber=1', + }) + ); + expect(updateSettings).toHaveBeenCalledWith( + expect.objectContaining({ + mpvPlayerArguments: '--screen=1\n--geometry=1280x720', + vlcPlayerArguments: '--qt-fullscreen-screennumber=1', + }) + ); + }); }); diff --git a/apps/web/src/app/settings/settings.component.ts b/apps/web/src/app/settings/settings.component.ts index b8f83ab0b..61c48f3b1 100644 --- a/apps/web/src/app/settings/settings.component.ts +++ b/apps/web/src/app/settings/settings.component.ts @@ -47,6 +47,7 @@ import { EmbeddedMpvSupport, CoverSize, Language, + normalizeExternalPlayerArguments, StartupBehavior, StreamFormat, Theme, @@ -187,8 +188,10 @@ export class SettingsComponent implements OnInit, OnDestroy { showExternalPlaybackBar: true, theme: Theme.SystemTheme, mpvPlayerPath: '', + mpvPlayerArguments: '', mpvReuseInstance: false, vlcPlayerPath: '', + vlcPlayerArguments: '', vlcReuseInstance: false, remoteControl: false, remoteControlPort: [ @@ -465,13 +468,21 @@ export class SettingsComponent implements OnInit, OnDestroy { * the indexed db store */ onSubmit(): void { - const settings = this.settingsForm.value; - const mpvPlayerPath = this.normalizeExternalPlayerPath( - settings.mpvPlayerPath - ); - const vlcPlayerPath = this.normalizeExternalPlayerPath( - settings.vlcPlayerPath - ); + const settings = { + ...this.settingsForm.value, + mpvPlayerPath: this.normalizeExternalPlayerPath( + this.settingsForm.value.mpvPlayerPath + ), + vlcPlayerPath: this.normalizeExternalPlayerPath( + this.settingsForm.value.vlcPlayerPath + ), + mpvPlayerArguments: normalizeExternalPlayerArguments( + this.settingsForm.value.mpvPlayerArguments + ), + vlcPlayerArguments: normalizeExternalPlayerArguments( + this.settingsForm.value.vlcPlayerArguments + ), + }; this.settingsStore.updateSettings(settings).then(() => { this.applyChangedSettings(); @@ -479,8 +490,8 @@ export class SettingsComponent implements OnInit, OnDestroy { if (window.electron) { window.electron.updateSettings(settings); - window.electron.setMpvPlayerPath(mpvPlayerPath); - window.electron.setVlcPlayerPath(vlcPlayerPath); + window.electron.setMpvPlayerPath(settings.mpvPlayerPath); + window.electron.setVlcPlayerPath(settings.vlcPlayerPath); } }); if (this.isDialog) { diff --git a/apps/web/src/assets/i18n/ar.json b/apps/web/src/assets/i18n/ar.json index 89077016b..723daab4f 100644 --- a/apps/web/src/assets/i18n/ar.json +++ b/apps/web/src/assets/i18n/ar.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "حدد مسار MPV. على macOS يمكنك استخدام حزمة تطبيق MPV مثل /Applications/mpv.app أو مسار الملف التنفيذي.", "MPV_COMPATIBLE_PLAYER_TIP": "يمكن تشغيل IINA كمشغل متوافق مع MPV على macOS، لكن استخدم مسار الملف التنفيذي مثل /Applications/IINA.app/Contents/MacOS/iina-cli أو /Applications/IINA.app/Contents/MacOS/IINA. عناصر تحكم IPTVnator وتتبع الموضع وإعادة استخدام النافذة مضمونة فقط مع MPV IPC.", "MPV_PLAYER_PATH": "مسار مشغل MPV", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "إعادة استخدام نسخة MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "عند التفعيل، سيتم إعادة استخدام نفس نافذة MPV لتشغيل البثوث المختلفة. عند التعطيل، يفتح كل بث في نافذة MPV جديدة.", "VLC_PLAYER_PATH_LABEL": "مسار VLC", "VLC_PLAYER_PATH_DESCRIPTION": "حدد مسار VLC. على macOS يمكنك استخدام حزمة تطبيق VLC مثل /Applications/VLC.app أو مسار الملف التنفيذي.", "VLC_PLAYER_PATH": "مسار مشغل VLC", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "إعادة استخدام نسخة VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "عند التفعيل، سيتم إعادة استخدام نفس نافذة VLC لتشغيل البثوث المختلفة. عند التعطيل، يفتح كل بث في نافذة VLC جديدة.", "REMOTE_CONTROL": "التحكم عن بعد", diff --git a/apps/web/src/assets/i18n/ary.json b/apps/web/src/assets/i18n/ary.json index d0e98eae8..e21c67da6 100644 --- a/apps/web/src/assets/i18n/ary.json +++ b/apps/web/src/assets/i18n/ary.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "حدد مسار MPV. فـ macOS تقدر تستعمل باقة تطبيق MPV بحال /Applications/mpv.app أو مسار الملف التنفيذي.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA يقدر يتفتح كمشغل متوافق مع MPV فـ macOS، ولكن استعمل مسار الملف التنفيذي بحال /Applications/IINA.app/Contents/MacOS/iina-cli أو /Applications/IINA.app/Contents/MacOS/IINA. تحكم IPTVnator وتتبع الموضع وإعادة استعمال النافذة مضمونين غير مع MPV IPC.", "MPV_PLAYER_PATH": "مسار مشغل mpv", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "إعادة استخدام نسخة MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "إلى كانت مفعلة، نفس نافذة MPV غادي تستعمل لتشغيل بثوث مختلفة. إلى كانت معطلة، كل بث كيتحل في نافذة MPV جديدة.", "VLC_PLAYER_PATH_LABEL": "مسار VLC", "VLC_PLAYER_PATH_DESCRIPTION": "حدد مسار VLC. فـ macOS تقدر تستعمل باقة تطبيق VLC بحال /Applications/VLC.app أو مسار الملف التنفيذي.", "VLC_PLAYER_PATH": "مسار مشغل VLC", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "إعادة استخدام نسخة VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "إلى كانت مفعلة، نفس نافذة VLC غادي تستعمل لتشغيل بثوث مختلفة. إلى كانت معطلة، كل بث كيتحل في نافذة VLC جديدة.", "REMOTE_CONTROL": "التحكم عن بعد", diff --git a/apps/web/src/assets/i18n/by.json b/apps/web/src/assets/i18n/by.json index 4d12859fd..38a14c918 100644 --- a/apps/web/src/assets/i18n/by.json +++ b/apps/web/src/assets/i18n/by.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Укажыце шлях да MPV. На macOS можна выкарыстоўваць пакет праграмы MPV, напрыклад /Applications/mpv.app, або шлях да выканальнага файла.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA можна запускаць як MPV-сумяшчальны прайгравальнік на macOS, але выкарыстоўвайце шлях да выканальнага файла, напрыклад /Applications/IINA.app/Contents/MacOS/iina-cli або /Applications/IINA.app/Contents/MacOS/IINA. Кіраванне IPTVnator, апытанне пазіцыі і паўторнае выкарыстанне акна гарантаваныя толькі для MPV IPC.", "MPV_PLAYER_PATH": "Шлях да mpv прайгравальніка", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Паўторна выкарыстоўваць асобнік MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Калі ўключана, тое ж акно MPV будзе паўторна выкарыстоўвацца для прайгравання розных патокаў. Калі адключана, кожны паток адкрываецца ў новым акне MPV.", "VLC_PLAYER_PATH_LABEL": "Шлях да VLC прайгравальніка", "VLC_PLAYER_PATH_DESCRIPTION": "Укажыце шлях да VLC. На macOS можна выкарыстоўваць пакет праграмы VLC, напрыклад /Applications/VLC.app, або шлях да выканальнага файла.", "VLC_PLAYER_PATH": "Шлях да VLC прайгравальніка", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Паўторна выкарыстоўваць асобнік VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Калі ўключана, тое ж акно VLC будзе паўторна выкарыстоўвацца для прайгравання розных патокаў. Калі адключана, кожны паток адкрываецца ў новым акне VLC.", "REMOTE_CONTROL": "Аддаленае кіраванне", diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json index 1a0b45ffb..6cbf84c63 100644 --- a/apps/web/src/assets/i18n/de.json +++ b/apps/web/src/assets/i18n/de.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Lege den Pfad zu MPV fest. Unter macOS kannst du das MPV-App-Bundle wie /Applications/mpv.app oder den ausführbaren Pfad verwenden.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA kann unter macOS als MPV-kompatibler Player gestartet werden, verwende dafür aber den ausführbaren Pfad wie /Applications/IINA.app/Contents/MacOS/iina-cli oder /Applications/IINA.app/Contents/MacOS/IINA. IPTVnator-Steuerung, Positionsabfrage und Wiederverwendung der Instanz sind nur für MPV IPC garantiert.", "MPV_PLAYER_PATH": "MPV-Player-Pfad", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "MPV-Instanz wiederverwenden", "MPV_REUSE_INSTANCE_DESCRIPTION": "Wenn aktiviert, wird dasselbe MPV-Fenster zum Abspielen verschiedener Streams wiederverwendet. Wenn deaktiviert, wird jeder Stream in einem neuen MPV-Fenster geöffnet.", "VLC_PLAYER_PATH_LABEL": "VLC-Pfad", "VLC_PLAYER_PATH_DESCRIPTION": "Lege den Pfad zu VLC fest. Unter macOS kannst du das VLC-App-Bundle wie /Applications/VLC.app oder den ausführbaren Pfad verwenden.", "VLC_PLAYER_PATH": "VLC-Playerpfad", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "VLC-Instanz wiederverwenden", "VLC_REUSE_INSTANCE_DESCRIPTION": "Wenn aktiviert, wird dasselbe VLC-Fenster zum Abspielen verschiedener Streams wiederverwendet. Wenn deaktiviert, wird jeder Stream in einem neuen VLC-Fenster geöffnet.", "REMOTE_CONTROL": "Fernsteuerung", diff --git a/apps/web/src/assets/i18n/el.json b/apps/web/src/assets/i18n/el.json index 73fe688b5..169dd5d95 100644 --- a/apps/web/src/assets/i18n/el.json +++ b/apps/web/src/assets/i18n/el.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Ορίστε τη διαδρομή προς το MPV. Στο macOS μπορείτε να χρησιμοποιήσετε το πακέτο εφαρμογής MPV, όπως /Applications/mpv.app, ή τη διαδρομή του εκτελέσιμου αρχείου.", "MPV_COMPATIBLE_PLAYER_TIP": "Το IINA μπορεί να εκκινηθεί ως συμβατό με MPV πρόγραμμα αναπαραγωγής στο macOS, αλλά χρησιμοποιήστε τη διαδρομή του εκτελέσιμου, όπως /Applications/IINA.app/Contents/MacOS/iina-cli ή /Applications/IINA.app/Contents/MacOS/IINA. Τα στοιχεία ελέγχου του IPTVnator, η παρακολούθηση θέσης και η επαναχρησιμοποίηση instance είναι εγγυημένα μόνο για MPV IPC.", "MPV_PLAYER_PATH": "διαδρομή mpv προγράμματος αναπαραγωγής", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Επαναχρησιμοποίηση της παρουσίας MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Όταν είναι ενεργοποιημένη, το ίδιο παράθυρο MPV θα επαναχρησιμοποιηθεί για την αναπαραγωγή διαφορετικών ροών. Όταν είναι απενεργοποιημένη, κάθε ροή ανοίγει σε ένα νέο παράθυρο MPV.", "VLC_PLAYER_PATH_LABEL": "διαδρομή VLC", "VLC_PLAYER_PATH_DESCRIPTION": "Ορίστε τη διαδρομή προς το VLC. Στο macOS μπορείτε να χρησιμοποιήσετε το πακέτο εφαρμογής VLC, όπως /Applications/VLC.app, ή τη διαδρομή του εκτελέσιμου αρχείου.", "VLC_PLAYER_PATH": "διαδρομή VLC player", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Επαναχρησιμοποίηση της παρουσίας VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Όταν είναι ενεργοποιημένη, το ίδιο παράθυρο VLC θα επαναχρησιμοποιηθεί για την αναπαραγωγή διαφορετικών ροών. Όταν είναι απενεργοποιημένη, κάθε ροή ανοίγει σε ένα νέο παράθυρο VLC.", "REMOTE_CONTROL": "Τηλεχειριστήριο", diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index 3ac2322f6..faf73197b 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -323,6 +323,9 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Set the path to MPV. On macOS you can use the MPV app bundle, such as /Applications/mpv.app, or the executable path.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA can be launched as an MPV-compatible player on macOS, but use its executable path, such as /Applications/IINA.app/Contents/MacOS/iina-cli or /Applications/IINA.app/Contents/MacOS/IINA. IPTVnator controls, position polling, and reuse-instance behavior are guaranteed only for MPV IPC.", "MPV_PLAYER_PATH": "mpv player path", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Reuse MPV instance", "MPV_REUSE_INSTANCE_DESCRIPTION": "When enabled, the same MPV window will be reused for playing different streams. When disabled, each stream opens in a new MPV window.", "RECORDING_FOLDER_LABEL": "Recording folder", @@ -332,6 +335,10 @@ "VLC_PLAYER_PATH_LABEL": "VLC path", "VLC_PLAYER_PATH_DESCRIPTION": "Set the path to VLC. On macOS you can use the VLC app bundle, such as /Applications/VLC.app, or the executable path.", "VLC_PLAYER_PATH": "VLC player path", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Reuse VLC instance", "VLC_REUSE_INSTANCE_DESCRIPTION": "When enabled, the same VLC window will be reused for playing different streams. When disabled, each stream opens in a new VLC window.", "REMOTE_CONTROL": "Remote control", diff --git a/apps/web/src/assets/i18n/es.json b/apps/web/src/assets/i18n/es.json index 23dbf76e0..7ccfed620 100644 --- a/apps/web/src/assets/i18n/es.json +++ b/apps/web/src/assets/i18n/es.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Establece la ruta a MPV. En macOS puedes usar el paquete de la app MPV, como /Applications/mpv.app, o la ruta del ejecutable.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA puede iniciarse como reproductor compatible con MPV en macOS, pero usa su ruta ejecutable, como /Applications/IINA.app/Contents/MacOS/iina-cli o /Applications/IINA.app/Contents/MacOS/IINA. Los controles de IPTVnator, el seguimiento de posición y la reutilización de instancia solo están garantizados con MPV IPC.", "MPV_PLAYER_PATH": "ruta del reproductor mpv", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Reutilizar instancia de MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Cuando esta opción está activada, se reutiliza la misma ventana de MPV para reproducir distintos streams. Cuando está desactivada, cada stream se abre en una nueva ventana de MPV.", "VLC_PLAYER_PATH_LABEL": "ruta VLC", "VLC_PLAYER_PATH_DESCRIPTION": "Establece la ruta a VLC. En macOS puedes usar el paquete de la app VLC, como /Applications/VLC.app, o la ruta del ejecutable.", "VLC_PLAYER_PATH": "Ruta del reproductor VLC", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Reutilizar instancia de VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Cuando esta opción está activada, se reutiliza la misma ventana de VLC para reproducir distintos streams. Cuando está desactivada, cada stream se abre en una nueva ventana de VLC.", "REMOTE_CONTROL": "Control remoto", diff --git a/apps/web/src/assets/i18n/fr.json b/apps/web/src/assets/i18n/fr.json index 8136fdde7..35e451955 100644 --- a/apps/web/src/assets/i18n/fr.json +++ b/apps/web/src/assets/i18n/fr.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Définissez le chemin vers MPV. Sur macOS, vous pouvez utiliser le bundle de l’app MPV, comme /Applications/mpv.app, ou le chemin de l’exécutable.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA peut être lancé comme lecteur compatible MPV sur macOS, mais utilisez son chemin exécutable, comme /Applications/IINA.app/Contents/MacOS/iina-cli ou /Applications/IINA.app/Contents/MacOS/IINA. Les contrôles IPTVnator, le suivi de position et la réutilisation d’instance ne sont garantis qu’avec MPV IPC.", "MPV_PLAYER_PATH": "Chemin du lecteur mpv", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Réutiliser l'instance MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Lorsqu'elle est activée, la même fenêtre MPV sera réutilisée pour lire différents flux. Lorsqu'elle est désactivée, chaque flux s'ouvre dans une nouvelle fenêtre MPV.", "VLC_PLAYER_PATH_LABEL": "Chemin VLC", "VLC_PLAYER_PATH_DESCRIPTION": "Définissez le chemin vers VLC. Sur macOS, vous pouvez utiliser le bundle de l’app VLC, comme /Applications/VLC.app, ou le chemin de l’exécutable.", "VLC_PLAYER_PATH": "Chemin du lecteur VLC", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Réutiliser l'instance VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Lorsqu'elle est activée, la même fenêtre VLC sera réutilisée pour lire différents flux. Lorsqu'elle est désactivée, chaque flux s'ouvre dans une nouvelle fenêtre VLC.", "REMOTE_CONTROL": "Télécommande", diff --git a/apps/web/src/assets/i18n/it.json b/apps/web/src/assets/i18n/it.json index dc686b2c6..81b47e2ec 100644 --- a/apps/web/src/assets/i18n/it.json +++ b/apps/web/src/assets/i18n/it.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Imposta il percorso di MPV. Su macOS puoi usare il bundle dell’app MPV, come /Applications/mpv.app, oppure il percorso dell’eseguibile.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA può essere avviato come player compatibile con MPV su macOS, ma usa il suo percorso eseguibile, come /Applications/IINA.app/Contents/MacOS/iina-cli o /Applications/IINA.app/Contents/MacOS/IINA. I controlli di IPTVnator, il polling della posizione e il riuso dell’istanza sono garantiti solo con MPV IPC.", "MPV_PLAYER_PATH": "percorso riproduttore mpv", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Riutilizza istanza MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Quando attivo, la stessa finestra MPV viene riutilizzata per riprodurre stream diversi. Quando disattivo, ogni stream viene aperto in una nuova finestra MPV.", "VLC_PLAYER_PATH_LABEL": "Percorso VLC", "VLC_PLAYER_PATH_DESCRIPTION": "Imposta il percorso di VLC. Su macOS puoi usare il bundle dell’app VLC, come /Applications/VLC.app, oppure il percorso dell’eseguibile.", "VLC_PLAYER_PATH": "Percorso lettore VLC", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Riutilizza istanza VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Quando attivo, la stessa finestra VLC viene riutilizzata per riprodurre stream diversi. Quando disattivo, ogni stream viene aperto in una nuova finestra VLC.", "REMOTE_CONTROL": "Telecomando", diff --git a/apps/web/src/assets/i18n/ja.json b/apps/web/src/assets/i18n/ja.json index a927ee680..653ae142e 100644 --- a/apps/web/src/assets/i18n/ja.json +++ b/apps/web/src/assets/i18n/ja.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "MPV へのパスを設定します。macOS では /Applications/mpv.app のような MPV アプリバンドル、または実行ファイルのパスを使用できます。", "MPV_COMPATIBLE_PLAYER_TIP": "IINA は macOS で MPV 互換プレーヤーとして起動できますが、/Applications/IINA.app/Contents/MacOS/iina-cli または /Applications/IINA.app/Contents/MacOS/IINA のような実行ファイルのパスを指定してください。IPTVnator の操作、位置ポーリング、インスタンス再利用は MPV IPC でのみ保証されます。", "MPV_PLAYER_PATH": "mpvプレーヤーのパス", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "MPVインスタンスを再利用", "MPV_REUSE_INSTANCE_DESCRIPTION": "有効にすると、異なるストリームの再生に同じMPVウィンドウが再利用されます。無効にすると、各ストリームが新しいMPVウィンドウで開きます。", "VLC_PLAYER_PATH_LABEL": "VLCのパス", "VLC_PLAYER_PATH_DESCRIPTION": "VLC へのパスを設定します。macOS では /Applications/VLC.app のような VLC アプリバンドル、または実行ファイルのパスを使用できます。", "VLC_PLAYER_PATH": "VLCプレーヤーのパス", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "VLCインスタンスを再利用", "VLC_REUSE_INSTANCE_DESCRIPTION": "有効にすると、異なるストリームの再生に同じVLCウィンドウが再利用されます。無効にすると、各ストリームが新しいVLCウィンドウで開きます。", "REMOTE_CONTROL": "リモートコントロール", diff --git a/apps/web/src/assets/i18n/ko.json b/apps/web/src/assets/i18n/ko.json index 22d98d4da..73b698b3f 100644 --- a/apps/web/src/assets/i18n/ko.json +++ b/apps/web/src/assets/i18n/ko.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "MPV 경로를 설정합니다. macOS에서는 /Applications/mpv.app 같은 MPV 앱 번들이나 실행 파일 경로를 사용할 수 있습니다.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA는 macOS에서 MPV 호환 플레이어로 실행할 수 있지만 /Applications/IINA.app/Contents/MacOS/iina-cli 또는 /Applications/IINA.app/Contents/MacOS/IINA 같은 실행 파일 경로를 사용하세요. IPTVnator 컨트롤, 위치 폴링, 인스턴스 재사용 동작은 MPV IPC에서만 보장됩니다.", "MPV_PLAYER_PATH": "mpv 플레이어 경로", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "MPV 인스턴스 재사용", "MPV_REUSE_INSTANCE_DESCRIPTION": "활성화하면 동일한 MPV 창에서 다른 스트림을 재생합니다. 비활성화하면 각 스트림이 새 MPV 창에서 열립니다.", "VLC_PLAYER_PATH_LABEL": "VLC 경로", "VLC_PLAYER_PATH_DESCRIPTION": "VLC 경로를 설정합니다. macOS에서는 /Applications/VLC.app 같은 VLC 앱 번들이나 실행 파일 경로를 사용할 수 있습니다.", "VLC_PLAYER_PATH": "VLC 플레이어 경로", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "VLC 인스턴스 재사용", "VLC_REUSE_INSTANCE_DESCRIPTION": "활성화하면 동일한 VLC 창에서 다른 스트림을 재생합니다. 비활성화하면 각 스트림이 새 VLC 창에서 열립니다.", "REMOTE_CONTROL": "원격 제어", diff --git a/apps/web/src/assets/i18n/nl.json b/apps/web/src/assets/i18n/nl.json index 9263f1308..e4a4f28e2 100644 --- a/apps/web/src/assets/i18n/nl.json +++ b/apps/web/src/assets/i18n/nl.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Stel het pad naar MPV in. Op macOS kun je de MPV-appbundel gebruiken, zoals /Applications/mpv.app, of het pad naar het uitvoerbare bestand.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA kan op macOS als MPV-compatibele speler worden gestart, maar gebruik het uitvoerbare pad, zoals /Applications/IINA.app/Contents/MacOS/iina-cli of /Applications/IINA.app/Contents/MacOS/IINA. IPTVnator-bediening, positie-polling en hergebruik van de instantie zijn alleen gegarandeerd met MPV IPC.", "MPV_PLAYER_PATH": "mpv-speler bestandspad", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "MPV-instantie hergebruiken", "MPV_REUSE_INSTANCE_DESCRIPTION": "Wanneer ingeschakeld, wordt hetzelfde MPV-venster hergebruikt voor het afspelen van verschillende streams. Wanneer uitgeschakeld, wordt elke stream geopend in een nieuw MPV-venster.", "VLC_PLAYER_PATH_LABEL": "VLC-bestandspad", "VLC_PLAYER_PATH_DESCRIPTION": "Stel het pad naar VLC in. Op macOS kun je de VLC-appbundel gebruiken, zoals /Applications/VLC.app, of het pad naar het uitvoerbare bestand.", "VLC_PLAYER_PATH": "VLC-speler bestandspad", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "VLC-instantie hergebruiken", "VLC_REUSE_INSTANCE_DESCRIPTION": "Wanneer ingeschakeld, wordt hetzelfde VLC-venster hergebruikt voor het afspelen van verschillende streams. Wanneer uitgeschakeld, wordt elke stream geopend in een nieuw VLC-venster.", "REMOTE_CONTROL": "Afstandsbediening", diff --git a/apps/web/src/assets/i18n/pl.json b/apps/web/src/assets/i18n/pl.json index 1df5bcd69..c0293fa21 100644 --- a/apps/web/src/assets/i18n/pl.json +++ b/apps/web/src/assets/i18n/pl.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Ustaw ścieżkę do MPV. W macOS możesz użyć pakietu aplikacji MPV, np. /Applications/mpv.app, albo ścieżki do pliku wykonywalnego.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA można uruchomić jako odtwarzacz zgodny z MPV w macOS, ale użyj ścieżki do pliku wykonywalnego, np. /Applications/IINA.app/Contents/MacOS/iina-cli lub /Applications/IINA.app/Contents/MacOS/IINA. Sterowanie IPTVnator, odpytywanie pozycji i ponowne użycie instancji są gwarantowane tylko dla MPV IPC.", "MPV_PLAYER_PATH": "Ścieżka odtwarzacza mpv", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Użyj ponownie instancji MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Po włączeniu to samo okno MPV będzie używane do odtwarzania różnych strumieni. Po wyłączeniu każdy strumień otwiera się w nowym oknie MPV.", "VLC_PLAYER_PATH_LABEL": "Ścieżka VLC", "VLC_PLAYER_PATH_DESCRIPTION": "Ustaw ścieżkę do VLC. W macOS możesz użyć pakietu aplikacji VLC, np. /Applications/VLC.app, albo ścieżki do pliku wykonywalnego.", "VLC_PLAYER_PATH": "Ścieżka odtwarzacza VLC", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Użyj ponownie instancji VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Po włączeniu to samo okno VLC będzie używane do odtwarzania różnych strumieni. Po wyłączeniu każdy strumień otwiera się w nowym oknie VLC.", "REMOTE_CONTROL": "Zdalne sterowanie", diff --git a/apps/web/src/assets/i18n/pt.json b/apps/web/src/assets/i18n/pt.json index d4dccf1aa..6cada2740 100644 --- a/apps/web/src/assets/i18n/pt.json +++ b/apps/web/src/assets/i18n/pt.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Defina o caminho para o MPV. No macOS você pode usar o pacote do app MPV, como /Applications/mpv.app, ou o caminho do executável.", "MPV_COMPATIBLE_PLAYER_TIP": "O IINA pode ser iniciado como player compatível com MPV no macOS, mas use o caminho do executável, como /Applications/IINA.app/Contents/MacOS/iina-cli ou /Applications/IINA.app/Contents/MacOS/IINA. Controles do IPTVnator, consulta de posição e reutilização de instância são garantidos apenas com MPV IPC.", "MPV_PLAYER_PATH": "Caminho do reprodutor mpv", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "Reutilizar instância do MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Quando ativado, a mesma janela do MPV será reutilizada para reproduzir streams diferentes. Quando desativado, cada stream abre em uma nova janela do MPV.", "VLC_PLAYER_PATH_LABEL": "Caminho do VLC", "VLC_PLAYER_PATH_DESCRIPTION": "Defina o caminho para o VLC. No macOS você pode usar o pacote do app VLC, como /Applications/VLC.app, ou o caminho do executável.", "VLC_PLAYER_PATH": "Caminho do reprodutor VLC", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "Reutilizar instância do VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Quando ativado, a mesma janela do VLC será reutilizada para reproduzir streams diferentes. Quando desativado, cada stream abre em uma nova janela do VLC.", "REMOTE_CONTROL": "Controle remoto", diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json index f80230efa..af1480a04 100644 --- a/apps/web/src/assets/i18n/ru.json +++ b/apps/web/src/assets/i18n/ru.json @@ -321,6 +321,9 @@ "MPV_PLAYER_PATH_DESCRIPTION": "Укажите путь к MPV. В macOS можно использовать пакет приложения MPV, например /Applications/mpv.app, или путь к исполняемому файлу.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA можно запускать как MPV-совместимый плеер в macOS, но указывайте путь к исполняемому файлу, например /Applications/IINA.app/Contents/MacOS/iina-cli или /Applications/IINA.app/Contents/MacOS/IINA. Управление из IPTVnator, опрос позиции и переиспользование экземпляра гарантированы только для MPV IPC.", "MPV_PLAYER_PATH": "Путь к mpv плееру", + "MPV_PLAYER_ARGUMENTS_LABEL": "Аргументы командной строки mpv", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Необязательные аргументы, которые передаются в mpv при запуске нового внешнего процесса плеера.", + "MPV_PLAYER_ARGUMENTS": "Аргументы mpv", "MPV_REUSE_INSTANCE_LABEL": "Переиспользовать экземпляр MPV", "MPV_REUSE_INSTANCE_DESCRIPTION": "Когда включено, одно и то же окно MPV будет использоваться для воспроизведения разных потоков. Когда отключено, каждый поток открывается в новом окне MPV.", "RECORDING_FOLDER_LABEL": "Папка для записей", @@ -330,6 +333,10 @@ "VLC_PLAYER_PATH_LABEL": "Путь к VLC плееру", "VLC_PLAYER_PATH_DESCRIPTION": "Укажите путь к VLC. В macOS можно использовать пакет приложения VLC, например /Applications/VLC.app, или путь к исполняемому файлу.", "VLC_PLAYER_PATH": "Путь к VLC плееру", + "VLC_PLAYER_ARGUMENTS_LABEL": "Аргументы командной строки VLC", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Необязательные аргументы, которые передаются в VLC при запуске нового внешнего процесса плеера.", + "VLC_PLAYER_ARGUMENTS": "Аргументы VLC", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Один аргумент на строку. Эти значения применяются перед параметрами IPTVnator для конкретного потока.", "VLC_REUSE_INSTANCE_LABEL": "Переиспользовать экземпляр VLC", "VLC_REUSE_INSTANCE_DESCRIPTION": "Когда включено, одно и то же окно VLC будет использоваться для воспроизведения разных потоков. Когда отключено, каждый поток открывается в новом окне VLC.", "REMOTE_CONTROL": "Удалённое управление", diff --git a/apps/web/src/assets/i18n/tr.json b/apps/web/src/assets/i18n/tr.json index eb5858e58..059d257f1 100644 --- a/apps/web/src/assets/i18n/tr.json +++ b/apps/web/src/assets/i18n/tr.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "MPV yolunu ayarlayın. macOS’ta /Applications/mpv.app gibi MPV uygulama paketini veya yürütülebilir dosya yolunu kullanabilirsiniz.", "MPV_COMPATIBLE_PLAYER_TIP": "IINA, macOS’ta MPV uyumlu bir oynatıcı olarak başlatılabilir; ancak /Applications/IINA.app/Contents/MacOS/iina-cli veya /Applications/IINA.app/Contents/MacOS/IINA gibi yürütülebilir dosya yolunu kullanın. IPTVnator kontrolleri, konum yoklama ve örnek yeniden kullanımı yalnızca MPV IPC için garanti edilir.", "MPV_PLAYER_PATH": "mpv oynatıcı yolu", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "MPV örneğini yeniden kullan", "MPV_REUSE_INSTANCE_DESCRIPTION": "Etkinleştirildiğinde, farklı yayınları oynatmak için aynı MPV penceresi yeniden kullanılır. Devre dışı bırakıldığında, her yayın yeni bir MPV penceresinde açılır.", "VLC_PLAYER_PATH_LABEL": "VLC yolu", "VLC_PLAYER_PATH_DESCRIPTION": "VLC yolunu ayarlayın. macOS’ta /Applications/VLC.app gibi VLC uygulama paketini veya yürütülebilir dosya yolunu kullanabilirsiniz.", "VLC_PLAYER_PATH": "VLC oynatıcı yolu", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "VLC örneğini yeniden kullan", "VLC_REUSE_INSTANCE_DESCRIPTION": "Etkinleştirildiğinde, farklı yayınları oynatmak için aynı VLC penceresi yeniden kullanılır. Devre dışı bırakıldığında, her yayın yeni bir VLC penceresinde açılır.", "REMOTE_CONTROL": "Uzaktan kontrol", diff --git a/apps/web/src/assets/i18n/zh.json b/apps/web/src/assets/i18n/zh.json index 29b003a69..a2096f120 100644 --- a/apps/web/src/assets/i18n/zh.json +++ b/apps/web/src/assets/i18n/zh.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "设置 MPV 路径。在 macOS 上可以使用 MPV 应用包,例如 /Applications/mpv.app,也可以使用可执行文件路径。", "MPV_COMPATIBLE_PLAYER_TIP": "IINA 可以在 macOS 上作为兼容 MPV 的播放器启动,但请使用它的可执行文件路径,例如 /Applications/IINA.app/Contents/MacOS/iina-cli 或 /Applications/IINA.app/Contents/MacOS/IINA。IPTVnator 控制、位置轮询和实例复用行为仅对 MPV IPC 保证可用。", "MPV_PLAYER_PATH": "mpv 播放器路径", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "复用 MPV 实例", "MPV_REUSE_INSTANCE_DESCRIPTION": "启用后,将复用同一个 MPV 窗口播放不同的视频流。禁用后,每个视频流都会在新的 MPV 窗口中打开。", "VLC_PLAYER_PATH_LABEL": "VLC路径", "VLC_PLAYER_PATH_DESCRIPTION": "设置 VLC 路径。在 macOS 上可以使用 VLC 应用包,例如 /Applications/VLC.app,也可以使用可执行文件路径。", "VLC_PLAYER_PATH": "VLC播放器路径", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "复用 VLC 实例", "VLC_REUSE_INSTANCE_DESCRIPTION": "启用后,将复用同一个 VLC 窗口播放不同的视频流。禁用后,每个视频流都会在新的 VLC 窗口中打开。", "REMOTE_CONTROL": "远程控制", diff --git a/apps/web/src/assets/i18n/zhtw.json b/apps/web/src/assets/i18n/zhtw.json index f0180760f..747260097 100644 --- a/apps/web/src/assets/i18n/zhtw.json +++ b/apps/web/src/assets/i18n/zhtw.json @@ -321,11 +321,18 @@ "MPV_PLAYER_PATH_DESCRIPTION": "設定 MPV 路徑。在 macOS 上可以使用 MPV App bundle,例如 /Applications/mpv.app,也可以使用可執行檔路徑。", "MPV_COMPATIBLE_PLAYER_TIP": "IINA 可以在 macOS 上作為相容 MPV 的播放器啟動,但請使用它的可執行檔路徑,例如 /Applications/IINA.app/Contents/MacOS/iina-cli 或 /Applications/IINA.app/Contents/MacOS/IINA。IPTVnator 控制、位置輪詢與 instance 重複使用僅保證適用於 MPV IPC。", "MPV_PLAYER_PATH": "mpv 播放器路徑", + "MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments", + "MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.", + "MPV_PLAYER_ARGUMENTS": "mpv arguments", "MPV_REUSE_INSTANCE_LABEL": "重複使用 MPV 實體", "MPV_REUSE_INSTANCE_DESCRIPTION": "啟用後,相同的 MPV 視窗將重複用於播放不同串流。停用時,每個串流會在新的 MPV 視窗中開啟。", "VLC_PLAYER_PATH_LABEL": "VLC 路徑", "VLC_PLAYER_PATH_DESCRIPTION": "設定 VLC 路徑。在 macOS 上可以使用 VLC App bundle,例如 /Applications/VLC.app,也可以使用可執行檔路徑。", "VLC_PLAYER_PATH": "VLC 播放器路徑", + "VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments", + "VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.", + "VLC_PLAYER_ARGUMENTS": "VLC arguments", + "EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.", "VLC_REUSE_INSTANCE_LABEL": "重複使用 VLC 實體", "VLC_REUSE_INSTANCE_DESCRIPTION": "啟用後,相同的 VLC 視窗將重複用於播放不同串流。停用時,每個串流會在新的 VLC 視窗中開啟。", "REMOTE_CONTROL": "遠端控制", diff --git a/docs/architecture/embedded-inline-playback.md b/docs/architecture/embedded-inline-playback.md index 76e8cff63..d06f52576 100644 --- a/docs/architecture/embedded-inline-playback.md +++ b/docs/architecture/embedded-inline-playback.md @@ -160,6 +160,24 @@ When a diagnostic is actionable in Electron, the inline banner may offer `Open i `PortalPlayer.openExternalPlayback(playback, player)` is the forced external launch API. It sends the playback payload to MPV or VLC regardless of the current saved player setting, so fallback buttons do not mutate preferences. +## External Player Arguments + +Electron settings expose optional MPV and VLC command-line argument fields only +when the corresponding external player is selected. The executable path remains a +path-only setting; extra flags are stored separately as `mpvPlayerArguments` and +`vlcPlayerArguments`. + +Argument fields are line-oriented: one non-empty trimmed line becomes one argv +entry. IPTVnator prepends those custom entries before its stream-specific runtime +arguments, then keeps the stream URL last. This avoids shell parsing, keeps paths +with spaces safe, and preserves existing settings for users who never configured +extra arguments. + +The arguments apply only when IPTVnator spawns a new external player process. If +MPV or VLC instance reuse is active and an existing process is reused, subsequent +streams are loaded through MPV IPC or VLC RC commands and new process arguments +are not re-applied until a fresh process starts. + ## Flatpak External Players Flatpak cannot execute host-installed `mpv` or `vlc` binaries directly from the sandbox. diff --git a/libs/services/src/lib/settings-store.service.ts b/libs/services/src/lib/settings-store.service.ts index 2b7daa772..414d7b1dc 100644 --- a/libs/services/src/lib/settings-store.service.ts +++ b/libs/services/src/lib/settings-store.service.ts @@ -29,8 +29,10 @@ const DEFAULT_SETTINGS: Settings = { showExternalPlaybackBar: true, theme: Theme.SystemTheme, mpvPlayerPath: '', + mpvPlayerArguments: '', mpvReuseInstance: false, vlcPlayerPath: '', + vlcPlayerArguments: '', vlcReuseInstance: false, remoteControl: false, remoteControlPort: 8765, @@ -136,8 +138,10 @@ export const SettingsStore = signalStore( showExternalPlaybackBar: store.showExternalPlaybackBar!(), theme: store.theme(), mpvPlayerPath: store.mpvPlayerPath(), + mpvPlayerArguments: store.mpvPlayerArguments(), mpvReuseInstance: store.mpvReuseInstance(), vlcPlayerPath: store.vlcPlayerPath(), + vlcPlayerArguments: store.vlcPlayerArguments(), vlcReuseInstance: store.vlcReuseInstance(), remoteControl: store.remoteControl(), remoteControlPort: store.remoteControlPort(), diff --git a/libs/shared/interfaces/src/index.ts b/libs/shared/interfaces/src/index.ts index ac1cb4fbd..02552fbb7 100644 --- a/libs/shared/interfaces/src/index.ts +++ b/libs/shared/interfaces/src/index.ts @@ -6,6 +6,7 @@ export * from './lib/epg-channel-with-programs.interface'; export * from './lib/epg-channel.model'; export * from './lib/epg-item.interface'; export * from './lib/epg-program.model'; +export * from './lib/external-player-arguments.utils'; export * from './lib/external-player-session.interface'; export * from './lib/indexed-db.config'; export * from './lib/ipc-command.class'; diff --git a/libs/shared/interfaces/src/lib/external-player-arguments.utils.spec.ts b/libs/shared/interfaces/src/lib/external-player-arguments.utils.spec.ts new file mode 100644 index 000000000..0942451c9 --- /dev/null +++ b/libs/shared/interfaces/src/lib/external-player-arguments.utils.spec.ts @@ -0,0 +1,33 @@ +import { + normalizeExternalPlayerArguments, + parseExternalPlayerArguments, +} from './external-player-arguments.utils'; + +describe('external player arguments utils', () => { + it('parses one argument per non-empty trimmed line', () => { + expect( + parseExternalPlayerArguments( + ' --screen=1\n\n--geometry=1280x720\r\n --hwdec=auto-safe ' + ) + ).toEqual(['--screen=1', '--geometry=1280x720', '--hwdec=auto-safe']); + }); + + it('normalizes arguments to newline-separated trimmed lines', () => { + expect( + normalizeExternalPlayerArguments([ + ' --screen=1 ', + '', + ' --geometry=1280x720 ', + ]) + ).toBe('--screen=1\n--geometry=1280x720'); + }); + + it('treats missing or unsupported values as empty arguments', () => { + expect(parseExternalPlayerArguments(undefined)).toEqual([]); + expect(parseExternalPlayerArguments(null)).toEqual([]); + expect(parseExternalPlayerArguments(123 as unknown as string)).toEqual( + [] + ); + expect(normalizeExternalPlayerArguments(' \n ')).toBe(''); + }); +}); diff --git a/libs/shared/interfaces/src/lib/external-player-arguments.utils.ts b/libs/shared/interfaces/src/lib/external-player-arguments.utils.ts new file mode 100644 index 000000000..8c48ab5ae --- /dev/null +++ b/libs/shared/interfaces/src/lib/external-player-arguments.utils.ts @@ -0,0 +1,28 @@ +export type ExternalPlayerArgumentsInput = + | string + | readonly unknown[] + | null + | undefined; + +export function parseExternalPlayerArguments( + value: ExternalPlayerArgumentsInput +): string[] { + if (Array.isArray(value)) { + return value.map((argument) => String(argument).trim()).filter(Boolean); + } + + if (typeof value !== 'string') { + return []; + } + + return value + .split(/\r?\n/) + .map((argument) => argument.trim()) + .filter(Boolean); +} + +export function normalizeExternalPlayerArguments( + value: ExternalPlayerArgumentsInput +): string { + return parseExternalPlayerArguments(value).join('\n'); +} diff --git a/libs/shared/interfaces/src/lib/settings.interface.ts b/libs/shared/interfaces/src/lib/settings.interface.ts index c2df9479a..6f8e44a20 100644 --- a/libs/shared/interfaces/src/lib/settings.interface.ts +++ b/libs/shared/interfaces/src/lib/settings.interface.ts @@ -37,8 +37,18 @@ export interface Settings { showExternalPlaybackBar?: boolean; theme: Theme; mpvPlayerPath: string; + /** + * Extra MPV CLI arguments entered one argument per line. Applied only when + * starting a new external MPV process. + */ + mpvPlayerArguments: string; mpvReuseInstance: boolean; vlcPlayerPath: string; + /** + * Extra VLC CLI arguments entered one argument per line. Applied only when + * starting a new external VLC process. + */ + vlcPlayerArguments: string; vlcReuseInstance: boolean; remoteControl: boolean; remoteControlPort: number;