chore: tighten validation and runtime logging

* chore: tighten validation and runtime logging

* fix(i18n): localize new settings labels
This commit is contained in:
4gray authored and GitHub committed 2026-05-15 17:43:42 +02:00
1 parent d24c77a143
commit 2d5c4fa4f9
52 files changed
+767 -214

No files matched your search

+3
View File
@@ -11,6 +11,9 @@
"targets": {
"e2e": {
"dependsOn": ["electron-backend:build-e2e"]
},
"lint": {
"executor": "@nx/eslint:lint"
}
},
"tags": ["scope:e2e", "domain:electron-main", "type:e2e"]
+1
View File
@@ -3,6 +3,7 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/electron-backend/src",
"projectType": "application",
"tags": ["scope:electron", "domain:electron-main", "type:app"],
"targets": {
"build-worker": {
"executor": "nx:run-commands",
@@ -22,6 +22,7 @@ import { ChildProcess, spawn } from 'child_process';
import { existsSync } from 'fs';
import { AddressInfo, createConnection, createServer } from 'net';
import path from 'path';
import { isExternalPlayerTraceEnabled, trace } from '../services/debug-trace';
import { getStalkerPlaybackContextHeaders } from '../services/stalker-playback-context.service';
import { ExternalPlayerSessionRegistry } from './external-player-session-registry';
@@ -40,6 +41,14 @@ let positionPollingInterval: NodeJS.Timeout | null = null;
let vlcProcess: ChildProcess | null = null;
let vlcRcPort: number | null = null;
function traceExternalPlayer(message: string, payload?: unknown): void {
if (!isExternalPlayerTraceEnabled()) {
return;
}
trace('external-player', message, payload);
}
interface ExternalPlaybackSnapshot {
positionSeconds: number;
durationSeconds: number | null;
@@ -291,7 +300,12 @@ function sendVlcRcCommand(port: number, command: string): Promise<void> {
settled = true;
clearTimeout(timeoutHandle);
if (!client.destroyed) client.destroy();
err ? reject(err) : resolve();
if (err) {
reject(err);
return;
}
resolve();
};
const timeoutHandle = setTimeout(
@@ -408,7 +422,6 @@ async function getMpvProperty(
let data = '';
client.on('connect', () => {
// console.log('[PlayerEvents] MPV IPC connected, sending:', request.trim());
client.write(request);
});
@@ -535,14 +548,11 @@ function startPositionPolling(
// Initial delay to let video load
setTimeout(() => {
// console.log('[PlayerEvents] Starting position polling interval');
positionPollingInterval = setInterval(async () => {
try {
const position = await getMpvProperty(socketPath, 'time-pos');
const duration = await getMpvProperty(socketPath, 'duration');
// console.log(`[PlayerEvents] Polling MPV: pos=${position}, dur=${duration}`);
if (position !== null && App.mainWindow) {
sendPlaybackPositionUpdate(sessionId, contentInfo, {
positionSeconds: Math.floor(position),
@@ -550,12 +560,11 @@ function startPositionPolling(
});
}
} catch (err) {
// console.error('[PlayerEvents] Error during polling:', err);
// MPV may have closed, stop polling
stopPositionPolling();
}
}, 5000); // Changed to 5 seconds for debugging
}, 2000); // Reduced initial delay
}, 5000);
}, 2000);
}
// VLC Polling
@@ -643,12 +652,6 @@ function startVlcPositionPolling(
// VLC needs time to start and bind port
setTimeout(() => {
/*
console.log(
`[PlayerEvents] Starting VLC polling on port ${port}`,
contentInfo
);
*/
vlcPollingInterval = setInterval(async () => {
try {
const snapshot = await getVlcPlaybackSnapshot(port);
@@ -669,7 +672,6 @@ function startVlcPositionPolling(
stopVlcPositionPolling();
}
} catch (err) {
// console.error('[PlayerEvents] VLC polling error:', err);
stopVlcPositionPolling();
}
}, 2000);
@@ -692,7 +694,10 @@ function sendMpvCommand(
const request = JSON.stringify({ command: [command, ...args] }) + '\n';
client.on('connect', () => {
console.log('Connected to MPV socket, sending command:', request);
traceExternalPlayer('mpv ipc command', {
command,
argsCount: args.length,
});
client.write(request);
client.end();
resolve();
@@ -768,7 +773,7 @@ ipcMain.handle(
mergedHeaders
);
console.log('[MPV] Opening player', {
traceExternalPlayer('open mpv player', {
path: mpvLaunchContext.playerPath,
launchMode: mpvLaunchContext.mode,
requestedReuseInstance,
@@ -791,7 +796,7 @@ ipcMain.handle(
!mpvProcess.killed &&
mpvSocketPath
) {
console.log('Reusing existing MPV instance');
traceExternalPlayer('reuse existing mpv instance');
try {
if (effectiveUserAgent) {
await sendMpvCommand('set_property', [
@@ -828,8 +833,8 @@ ipcMain.handle(
}
await sendMpvCommand('loadfile', loadFileArgs);
console.log(
'Successfully loaded new URL in existing MPV instance'
traceExternalPlayer(
'loaded new url in existing mpv instance'
);
externalPlayerSessions.attachCloser(
@@ -881,7 +886,7 @@ ipcMain.handle(
}
// Create new MPV process
console.log('Creating new MPV instance');
traceExternalPlayer('create new mpv instance');
let socketPath: string | null = null;
const args: string[] = [];
@@ -954,7 +959,7 @@ ipcMain.handle(
continue;
}
console.log('[MPV stdout]:', output);
traceExternalPlayer('mpv stdout', { output });
// MPV sometimes outputs errors to stdout instead of stderr
if (
@@ -1016,7 +1021,7 @@ ipcMain.handle(
});
proc.on('exit', (code) => {
console.log(`MPV exited with code ${code}`);
traceExternalPlayer('mpv exited', { code });
mpvProcess = null;
mpvSocketPath = null;
stopPositionPolling();
@@ -1044,10 +1049,9 @@ ipcMain.handle(
if (reuseInstance && socketPath) {
mpvProcess = proc;
mpvSocketPath = socketPath;
console.log(
'Stored MPV process for reuse with socket:',
socketPath
);
traceExternalPlayer('stored mpv process for reuse', {
socketPath,
});
} else {
// Detach the process so it can continue running independently
proc.unref();
@@ -1092,18 +1096,20 @@ ipcMain.handle(
'SET_MPV_PLAYER_PATH',
(_event, mpvPlayerPath: string | null | undefined) => {
const normalizedPlayerPath = normalizePlayerPathForStore(mpvPlayerPath);
console.log('... setting mpv player path', normalizedPlayerPath);
traceExternalPlayer('set mpv player path', {
playerPath: normalizedPlayerPath,
});
store.set(MPV_PLAYER_PATH, normalizedPlayerPath);
}
);
ipcMain.handle('SET_MPV_REUSE_INSTANCE', (_event, reuseInstance: boolean) => {
console.log('... setting mpv reuse instance', reuseInstance);
traceExternalPlayer('set mpv reuse instance', { reuseInstance });
store.set(MPV_REUSE_INSTANCE, reuseInstance);
// If disabling reuse, kill the existing process
if (!reuseInstance && mpvProcess && !mpvProcess.killed) {
console.log('Disabling reuse, cleaning up existing MPV process');
traceExternalPlayer('clean up mpv process after disabling reuse');
mpvProcess.kill();
mpvProcess = null;
mpvSocketPath = null;
@@ -1167,7 +1173,7 @@ ipcMain.handle(
mergedHeaders['User-Agent'] ??
mergedHeaders['user-agent'] ??
undefined;
console.log('[VLC] Opening player', {
traceExternalPlayer('open vlc player', {
path: vlcLaunchContext.playerPath,
launchMode: vlcLaunchContext.mode,
requestedReuseInstance,
@@ -1190,10 +1196,9 @@ ipcMain.handle(
!vlcProcess.killed &&
vlcRcPort
) {
console.log(
'Reusing existing VLC instance on RC port',
vlcRcPort
);
traceExternalPlayer('reuse existing vlc instance', {
rcPort: vlcRcPort,
});
try {
const enqueueCommands = buildVlcEnqueueCommands({
url,
@@ -1205,8 +1210,8 @@ ipcMain.handle(
startTime,
});
await sendVlcRcCommands(vlcRcPort, enqueueCommands);
console.log(
'Successfully loaded new URL in existing VLC instance'
traceExternalPlayer(
'loaded new url in existing vlc instance'
);
const reusedRcPort = vlcRcPort;
@@ -1283,7 +1288,7 @@ ipcMain.handle(
if (contentInfo || reuseInstance) {
try {
rcPort = await getFreePort();
console.log('Using VLC RC port:', rcPort);
traceExternalPlayer('using vlc rc port', { rcPort });
} catch (e) {
console.error('Failed to get free port for VLC:', e);
}
@@ -1333,7 +1338,10 @@ ipcMain.handle(
args.push(`:meta-title=${title}`);
}
console.log('VLC Args:', args);
traceExternalPlayer('vlc args prepared', {
argCount: args.length,
hasRcPort: rcPort > 0,
});
let lastVlcSnapshot: ExternalPlaybackSnapshot | null = null;
// Wrap spawn in a promise to catch startup errors
@@ -1358,10 +1366,9 @@ ipcMain.handle(
if (trackProcess && rcPort > 0) {
vlcProcess = proc;
vlcRcPort = rcPort;
console.log(
'Tracking VLC process for reuse on RC port',
rcPort
);
traceExternalPlayer('tracking vlc process for reuse', {
rcPort,
});
}
const markVlcSessionClosed = () => {
@@ -1433,7 +1440,7 @@ ipcMain.handle(
proc.stdout.on('data', (data) => {
const output = data.toString().trim();
if (output) {
console.log('[VLC stdout]:', output);
traceExternalPlayer('vlc stdout', { output });
}
});
}
@@ -1455,7 +1462,9 @@ ipcMain.handle(
vlcRcPort = null;
}
if (!isRetry && rcPort > 0) {
console.log('Retrying VLC without RC interface...');
traceExternalPlayer(
'retry vlc without rc interface after start error'
);
// Retry without RC args
const retryArgs = playerArgs.filter(
(arg) =>
@@ -1480,7 +1489,7 @@ ipcMain.handle(
});
proc.on('exit', (code) => {
console.log(`VLC exited with code ${code}`);
traceExternalPlayer('vlc exited', { code });
if (vlcProcess === proc) {
vlcProcess = null;
vlcRcPort = null;
@@ -1501,8 +1510,8 @@ ipcMain.handle(
}
if (code === 1 && !isRetry && rcPort > 0) {
console.log(
'VLC exited with error, retrying without RC interface...'
traceExternalPlayer(
'retry vlc without rc interface after exit'
);
stopVlcPositionPolling();
const retryArgs = playerArgs.filter(
@@ -1559,18 +1568,20 @@ ipcMain.handle(
'SET_VLC_PLAYER_PATH',
(_event, vlcPlayerPath: string | null | undefined) => {
const normalizedPlayerPath = normalizePlayerPathForStore(vlcPlayerPath);
console.log('... setting vlc player path', normalizedPlayerPath);
traceExternalPlayer('set vlc player path', {
playerPath: normalizedPlayerPath,
});
store.set(VLC_PLAYER_PATH, normalizedPlayerPath);
}
);
ipcMain.handle('SET_VLC_REUSE_INSTANCE', (_event, reuseInstance: boolean) => {
console.log('... setting vlc reuse instance', reuseInstance);
traceExternalPlayer('set vlc reuse instance', { reuseInstance });
store.set(VLC_REUSE_INSTANCE, reuseInstance);
// If disabling reuse, kill the existing tracked process
if (!reuseInstance && vlcProcess && !vlcProcess.killed) {
console.log('Disabling reuse, cleaning up existing VLC process');
traceExternalPlayer('clean up vlc process after disabling reuse');
vlcProcess.kill();
vlcProcess = null;
vlcRcPort = null;
@@ -27,10 +27,7 @@ function summarizeObject(
const entries = Object.entries(value);
const summary: Record<string, unknown> = {};
if (
value.constructor?.name &&
value.constructor.name !== 'Object'
) {
if (value.constructor?.name && value.constructor.name !== 'Object') {
summary.__type = value.constructor.name;
}
@@ -73,6 +70,10 @@ export function isRendererConsoleTraceEnabled(): boolean {
return readFlag('IPTVNATOR_TRACE_RENDERER_CONSOLE');
}
export function isExternalPlayerTraceEnabled(): boolean {
return isStartupTraceEnabled() || readFlag('IPTVNATOR_TRACE_PLAYER');
}
export function roundTraceDuration(durationMs: number): number {
return Math.round(durationMs * 10) / 10;
}
@@ -81,10 +82,7 @@ export function compactSqlForTrace(sql: string): string {
return truncateString(sql.replace(/\s+/g, ' ').trim());
}
export function summarizeForTrace(
value: unknown,
depth = 0
): unknown {
export function summarizeForTrace(value: unknown, depth = 0): unknown {
if (
value == null ||
typeof value === 'boolean' ||
@@ -160,11 +158,7 @@ export function safeStringifyForTrace(payload: unknown): string {
}
}
export function trace(
scope: string,
message: string,
payload?: unknown
): void {
export function trace(scope: string, message: string, payload?: unknown): void {
if (payload === undefined) {
console.log(`${TRACE_PREFIX}[${scope}] ${message}`);
return;
+4 -1
View File
@@ -10,7 +10,10 @@
],
"// targets": "to see all targets run: nx show project web-e2e --web",
"targets": {
"e2e": {}
"e2e": {},
"lint": {
"executor": "@nx/eslint:lint"
}
},
"tags": ["scope:e2e", "domain:web", "type:e2e"]
}
+7 -5
View File
@@ -17,9 +17,12 @@ import {
Settings,
STORE_KEY,
Theme,
createDevLogger,
} from '@iptvnator/shared/interfaces';
import { SettingsService } from './services/settings.service';
const debugAppComponent = createDevLogger('AppComponent');
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
@@ -106,8 +109,7 @@ export class AppComponent implements OnInit {
// Settings are stored in IndexedDB and loaded by the settings store
// Only specific Electron settings (MPV/VLC paths) are sent when changed in settings component
const resolvedLang =
settings.language ?? this.DEFAULT_LANG;
const resolvedLang = settings.language ?? this.DEFAULT_LANG;
this.translate.use(resolvedLang);
// Mirror the active language to localStorage so the next
// cold start can read it synchronously in app.config.ts's
@@ -166,7 +168,7 @@ export class AppComponent implements OnInit {
const result = await window.electron.checkEpgFreshness(urls, 12);
if (result.freshUrls.length > 0) {
console.log(
debugAppComponent(
`EPG: ${result.freshUrls.length} source(s) already fresh, skipping fetch`
);
// Show snackbar if all EPG sources are fresh (no stale URLs)
@@ -180,7 +182,7 @@ export class AppComponent implements OnInit {
}
if (result.staleUrls.length > 0) {
console.log(
debugAppComponent(
`EPG: Fetching ${result.staleUrls.length} stale source(s)`
);
this.epgService.fetchEpg(result.staleUrls);
@@ -218,7 +220,7 @@ export class AppComponent implements OnInit {
// Trigger auto-update if there are playlists to update
if (playlistsToUpdate.length > 0) {
console.log(
debugAppComponent(
`Auto-updating ${playlistsToUpdate.length} playlist(s) on startup`
);
this.dataService.sendIpcEvent(
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "إصدار جديد متاح",
"LATEST_VERSION": "أنت تستخدم الإصدار الأحدث",
"STREAM_FORMAT": "تنسيق البث",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "تشغيل MPV/VLC بالنقر المزدوج",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "يتطلب النقر المزدوج على صفوف القنوات قبل بدء التشغيل عبر MPV أو VLC.",
"LANGUAGE": "اللغة",
"SETTINGS_SAVED": "نجاح! تم حفظ الإعدادات.",
"THEME": "السمة البصرية",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "إعادة استخدام نسخة MPV",
"MPV_REUSE_INSTANCE_DESCRIPTION": "عند التفعيل، سيتم إعادة استخدام نفس نافذة MPV لتشغيل البثوث المختلفة. عند التعطيل، يفتح كل بث في نافذة MPV جديدة.",
"RECORDING_FOLDER_LABEL": "مجلد التسجيلات",
"RECORDING_FOLDER_DESCRIPTION": "مجلد تسجيلات البث المباشر من MPV المضمن. اتركه فارغًا لاستخدام مجلد التنزيلات في النظام.",
"RECORDING_FOLDER": "مجلد التسجيلات",
"RECORDING_FOLDER_SELECT": "اختر مجلد التسجيلات",
"VLC_PLAYER_PATH_LABEL": "مسار VLC",
"VLC_PLAYER_PATH_DESCRIPTION": "حدد مسار VLC. على macOS يمكنك استخدام حزمة تطبيق VLC مثل /Applications/VLC.app أو مسار الملف التنفيذي.",
"VLC_PLAYER_PATH": "مسار مشغل VLC",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "كاين إصدار جديد متوفر",
"LATEST_VERSION": "راك تستعمل أحدث إصدار",
"STREAM_FORMAT": "صيغة البث",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "شغل MPV/VLC بدوبل كليك",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "خاص دير دوبل كليك على صفوف القنوات قبل ما يبدا التشغيل بـ MPV ولا VLC.",
"LANGUAGE": "اللغة",
"SETTINGS_SAVED": "نجاح! الإعدادات تحفضو.",
"THEME": "المظهر المرئي",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "إعادة استخدام نسخة MPV",
"MPV_REUSE_INSTANCE_DESCRIPTION": "إلى كانت مفعلة، نفس نافذة MPV غادي تستعمل لتشغيل بثوث مختلفة. إلى كانت معطلة، كل بث كيتحل في نافذة MPV جديدة.",
"RECORDING_FOLDER_LABEL": "مجلد التسجيلات",
"RECORDING_FOLDER_DESCRIPTION": "المجلد ديال تسجيلات البث المباشر من MPV المدمج. خليه خاوي باش تستعمل مجلد التنزيلات ديال النظام.",
"RECORDING_FOLDER": "مجلد التسجيلات",
"RECORDING_FOLDER_SELECT": "اختار مجلد التسجيلات",
"VLC_PLAYER_PATH_LABEL": "مسار VLC",
"VLC_PLAYER_PATH_DESCRIPTION": "حدد مسار VLC. فـ macOS تقدر تستعمل باقة تطبيق VLC بحال /Applications/VLC.app أو مسار الملف التنفيذي.",
"VLC_PLAYER_PATH": "مسار مشغل VLC",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Даступная новая версія праграмы",
"LATEST_VERSION": "Вы выкарыстоўваеце апошнюю версію праграмы",
"STREAM_FORMAT": "Фармат патоку",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Запускаць MPV/VLC падвойным націскам",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Патрабаваць падвойны націск па радках каналаў перад пачаткам прайгравання праз MPV або VLC.",
"LANGUAGE": "Мова",
"SETTINGS_SAVED": "Новыя налады былі паспяхова захаваны!",
"THEME": "Тэма афармлення",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "Паўторна выкарыстоўваць асобнік MPV",
"MPV_REUSE_INSTANCE_DESCRIPTION": "Калі ўключана, тое ж акно MPV будзе паўторна выкарыстоўвацца для прайгравання розных патокаў. Калі адключана, кожны паток адкрываецца ў новым акне MPV.",
"RECORDING_FOLDER_LABEL": "Папка для запісаў",
"RECORDING_FOLDER_DESCRIPTION": "Папка для запісаў жывых патокаў з убудаванага MPV. Пакіньце пустым, каб выкарыстоўваць сістэмную папку спампаванняў.",
"RECORDING_FOLDER": "Папка для запісаў",
"RECORDING_FOLDER_SELECT": "Выбраць папку для запісаў",
"VLC_PLAYER_PATH_LABEL": "Шлях да VLC прайгравальніка",
"VLC_PLAYER_PATH_DESCRIPTION": "Укажыце шлях да VLC. На macOS можна выкарыстоўваць пакет праграмы VLC, напрыклад /Applications/VLC.app, або шлях да выканальнага файла.",
"VLC_PLAYER_PATH": "Шлях да VLC прайгравальніка",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "EIne neue Version der Anwendung ist verfügbar",
"LATEST_VERSION": "Sie nutzen die neueste Version der Anwendung",
"STREAM_FORMAT": "Stream-Format",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "MPV/VLC per Doppelklick starten",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Erfordert einen Doppelklick auf Kanalzeilen, bevor die Wiedergabe mit MPV oder VLC startet.",
"LANGUAGE": "Sprache",
"SETTINGS_SAVED": "Einstellungen wurden erfolgreich gespeichert!",
"THEME": "Farbdesign",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Aufnahmeordner",
"RECORDING_FOLDER_DESCRIPTION": "Ordner für Live-Stream-Aufnahmen aus Embedded MPV. Leer lassen, um den systemweiten Downloads-Ordner zu verwenden.",
"RECORDING_FOLDER": "Aufnahmeordner",
"RECORDING_FOLDER_SELECT": "Aufnahmeordner auswählen",
"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",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Υπάρχει διαθέσιμη μια νέα έκδοση",
"LATEST_VERSION": "Χρησιμοποιείτε την πιο πρόσφατη έκδοση",
"STREAM_FORMAT": "Μορφή Stream",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Εκκίνηση MPV/VLC με διπλό κλικ",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Απαιτεί διπλό κλικ στις γραμμές καναλιών πριν ξεκινήσει η αναπαραγωγή με MPV ή VLC.",
"LANGUAGE": "Γλώσσα",
"SETTINGS_SAVED": "Επιτυχία! Η διαμόρφωση αποθηκεύτηκε.",
"THEME": "Οπτικό θέμα",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "Επαναχρησιμοποίηση της παρουσίας MPV",
"MPV_REUSE_INSTANCE_DESCRIPTION": "Όταν είναι ενεργοποιημένη, το ίδιο παράθυρο MPV θα επαναχρησιμοποιηθεί για την αναπαραγωγή διαφορετικών ροών. Όταν είναι απενεργοποιημένη, κάθε ροή ανοίγει σε ένα νέο παράθυρο MPV.",
"RECORDING_FOLDER_LABEL": "Φάκελος εγγραφών",
"RECORDING_FOLDER_DESCRIPTION": "Φάκελος για εγγραφές ζωντανών ροών από το ενσωματωμένο MPV. Αφήστε το κενό για να χρησιμοποιηθεί ο φάκελος Λήψεις του συστήματος.",
"RECORDING_FOLDER": "Φάκελος εγγραφών",
"RECORDING_FOLDER_SELECT": "Επιλογή φακέλου εγγραφών",
"VLC_PLAYER_PATH_LABEL": "διαδρομή VLC",
"VLC_PLAYER_PATH_DESCRIPTION": "Ορίστε τη διαδρομή προς το VLC. Στο macOS μπορείτε να χρησιμοποιήσετε το πακέτο εφαρμογής VLC, όπως /Applications/VLC.app, ή τη διαδρομή του εκτελέσιμου αρχείου.",
"VLC_PLAYER_PATH": "διαδρομή VLC player",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Hay una nueva versión disponible",
"LATEST_VERSION": "Estás usando la versión reciente",
"STREAM_FORMAT": "Formato de transmision",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Iniciar MPV/VLC con doble clic",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Requiere doble clic en las filas de canales antes de iniciar la reproducción con MPV o VLC.",
"LANGUAGE": "Idioma",
"SETTINGS_SAVED": "¡Completado! La configuración se guardó.",
"THEME": "Tema visual",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Carpeta de grabaciones",
"RECORDING_FOLDER_DESCRIPTION": "Carpeta para grabaciones de streams en vivo desde MPV integrado. Déjala vacía para usar la carpeta Descargas del sistema.",
"RECORDING_FOLDER": "Carpeta de grabaciones",
"RECORDING_FOLDER_SELECT": "Seleccionar carpeta de grabaciones",
"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",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Une nouvelle version est disponible",
"LATEST_VERSION": "Vous utilisez la dernière version",
"STREAM_FORMAT": "Format du flux",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Lancer MPV/VLC par double-clic",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Exiger un double-clic sur les lignes de chaînes avant de lancer la lecture avec MPV ou VLC.",
"LANGUAGE": "Langue",
"SETTINGS_SAVED": "La configuration a été enregistrée avec succès.",
"THEME": "Thème",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Dossier d’enregistrement",
"RECORDING_FOLDER_DESCRIPTION": "Dossier des enregistrements de flux en direct depuis MPV intégré. Laissez vide pour utiliser le dossier Téléchargements du système.",
"RECORDING_FOLDER": "Dossier d’enregistrement",
"RECORDING_FOLDER_SELECT": "Choisir le dossier d’enregistrement",
"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",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "È disponibile una nuova versione",
"LATEST_VERSION": "Questa versione è aggiornata",
"STREAM_FORMAT": "Formato streaming",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Avvia MPV/VLC con doppio clic",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Richiede un doppio clic sulle righe dei canali prima di avviare la riproduzione con MPV o VLC.",
"LANGUAGE": "Lingua",
"SETTINGS_SAVED": "La configurazione è stata salvata.",
"THEME": "Tema visuale",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Cartella registrazioni",
"RECORDING_FOLDER_DESCRIPTION": "Cartella per le registrazioni degli stream live da MPV integrato. Lascia vuoto per usare la cartella Download del sistema.",
"RECORDING_FOLDER": "Cartella registrazioni",
"RECORDING_FOLDER_SELECT": "Seleziona cartella registrazioni",
"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",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "新しいバージョンが利用可能です",
"LATEST_VERSION": "最新バージョンを使用中です",
"STREAM_FORMAT": "ストリーム形式",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "ダブルクリックで MPV/VLC を起動",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "MPV または VLC の再生を開始する前に、チャンネル行をダブルクリックする必要があります。",
"LANGUAGE": "言語",
"SETTINGS_SAVED": "成功!設定が保存されました。",
"THEME": "視覚テーマ",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "MPVインスタンスを再利用",
"MPV_REUSE_INSTANCE_DESCRIPTION": "有効にすると、異なるストリームの再生に同じMPVウィンドウが再利用されます。無効にすると、各ストリームが新しいMPVウィンドウで開きます。",
"RECORDING_FOLDER_LABEL": "録画フォルダー",
"RECORDING_FOLDER_DESCRIPTION": "Embedded MPV のライブストリーム録画を保存するフォルダーです。空のままにするとシステムのダウンロードフォルダーを使用します。",
"RECORDING_FOLDER": "録画フォルダー",
"RECORDING_FOLDER_SELECT": "録画フォルダーを選択",
"VLC_PLAYER_PATH_LABEL": "VLCのパス",
"VLC_PLAYER_PATH_DESCRIPTION": "VLC へのパスを設定します。macOS では /Applications/VLC.app のような VLC アプリバンドル、または実行ファイルのパスを使用できます。",
"VLC_PLAYER_PATH": "VLCプレーヤーのパス",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "새로운 버전이 있습니다",
"LATEST_VERSION": "최신 버전을 사용하고 있습니다",
"STREAM_FORMAT": "스트림 형식",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "두 번 클릭하여 MPV/VLC 시작",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "MPV 또는 VLC 재생을 시작하기 전에 채널 행을 두 번 클릭하도록 합니다.",
"LANGUAGE": "언어",
"SETTINGS_SAVED": "설정이 저장되었습니다.",
"THEME": "테마",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "MPV 인스턴스 재사용",
"MPV_REUSE_INSTANCE_DESCRIPTION": "활성화하면 동일한 MPV 창에서 다른 스트림을 재생합니다. 비활성화하면 각 스트림이 새 MPV 창에서 열립니다.",
"RECORDING_FOLDER_LABEL": "녹화 폴더",
"RECORDING_FOLDER_DESCRIPTION": "Embedded MPV의 라이브 스트림 녹화를 저장할 폴더입니다. 비워 두면 시스템 다운로드 폴더를 사용합니다.",
"RECORDING_FOLDER": "녹화 폴더",
"RECORDING_FOLDER_SELECT": "녹화 폴더 선택",
"VLC_PLAYER_PATH_LABEL": "VLC 경로",
"VLC_PLAYER_PATH_DESCRIPTION": "VLC 경로를 설정합니다. macOS에서는 /Applications/VLC.app 같은 VLC 앱 번들이나 실행 파일 경로를 사용할 수 있습니다.",
"VLC_PLAYER_PATH": "VLC 플레이어 경로",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Een nieuwe versie is beschikbaar",
"LATEST_VERSION": "Je gebruikt de nieuwste versie",
"STREAM_FORMAT": "Streamformaat",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "MPV/VLC starten met dubbelklik",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Vereist een dubbelklik op kanaalrijen voordat MPV- of VLC-weergave start.",
"LANGUAGE": "Taal",
"SETTINGS_SAVED": "Succes! De instellingen zijn opgeslagen.",
"THEME": "Visueel thema",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Opnamemap",
"RECORDING_FOLDER_DESCRIPTION": "Map voor live-streamopnames vanuit Embedded MPV. Laat leeg om de systeemmap Downloads te gebruiken.",
"RECORDING_FOLDER": "Opnamemap",
"RECORDING_FOLDER_SELECT": "Opnamemap selecteren",
"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",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Dostępna jest nowa wersja",
"LATEST_VERSION": "Używasz najnowszej wersji",
"STREAM_FORMAT": "Format strumienia",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Uruchamiaj MPV/VLC podwójnym kliknięciem",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Wymagaj podwójnego kliknięcia wiersza kanału przed rozpoczęciem odtwarzania w MPV lub VLC.",
"LANGUAGE": "Język",
"SETTINGS_SAVED": "Sukces! Konfiguracja została zapisana.",
"THEME": "Motyw wizualny",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Folder nagrań",
"RECORDING_FOLDER_DESCRIPTION": "Folder na nagrania strumieni na żywo z wbudowanego MPV. Pozostaw puste, aby użyć systemowego folderu Pobrane.",
"RECORDING_FOLDER": "Folder nagrań",
"RECORDING_FOLDER_SELECT": "Wybierz folder nagrań",
"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",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Há uma nova versão disponível",
"LATEST_VERSION": "Você está usando a versão mais recente",
"STREAM_FORMAT": "Formato de stream",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Iniciar MPV/VLC com duplo clique",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Exige um duplo clique nas linhas de canais antes de iniciar a reprodução no MPV ou VLC.",
"LANGUAGE": "Idioma",
"SETTINGS_SAVED": "Sucesso! A configuração foi salva.",
"THEME": "Tema visual",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Pasta de gravações",
"RECORDING_FOLDER_DESCRIPTION": "Pasta para gravações de streams ao vivo do MPV incorporado. Deixe em branco para usar a pasta Downloads do sistema.",
"RECORDING_FOLDER": "Pasta de gravações",
"RECORDING_FOLDER_SELECT": "Selecionar pasta de gravações",
"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",
+2
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Доступна новая версия приложения",
"LATEST_VERSION": "Вы используете последнюю версию приложения",
"STREAM_FORMAT": "Формат потока",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "Запускать MPV/VLC двойным щелчком",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "Требовать двойной щелчок по строке канала перед запуском воспроизведения через MPV или VLC.",
"LANGUAGE": "Язык",
"SETTINGS_SAVED": "Новые настройки были успешно сохранены!",
"THEME": "Тема оформления",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "Yeni bir sürüm mevcut",
"LATEST_VERSION": "En son sürümü kullanıyorsunuz",
"STREAM_FORMAT": "Yayın Formatı",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "MPV/VLC'yi çift tıklamayla başlat",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "MPV veya VLC oynatımı başlamadan önce kanal satırlarına çift tıklanmasını gerektirir.",
"LANGUAGE": "Dil",
"SETTINGS_SAVED": "Başarılı! Yapılandırma kaydedildi.",
"THEME": "Görünüm teması",
@@ -326,6 +328,10 @@
"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.",
"RECORDING_FOLDER_LABEL": "Kayıt klasörü",
"RECORDING_FOLDER_DESCRIPTION": "Yerleşik MPV'den canlı yayın kayıtlarının kaydedileceği klasör. Sistem İndirilenler klasörünü kullanmak için boş bırakın.",
"RECORDING_FOLDER": "Kayıt klasörü",
"RECORDING_FOLDER_SELECT": "Kayıt klasörü seç",
"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",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "有新版本可用 ",
"LATEST_VERSION": "您使用的是最新版本",
"STREAM_FORMAT": "流格式",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "双击启动 MPV/VLC",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "在 MPV 或 VLC 开始播放前,要求双击频道行。",
"LANGUAGE": "语言",
"SETTINGS_SAVED": "配置已成功保存",
"THEME": "主题",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "复用 MPV 实例",
"MPV_REUSE_INSTANCE_DESCRIPTION": "启用后,将复用同一个 MPV 窗口播放不同的视频流。禁用后,每个视频流都会在新的 MPV 窗口中打开。",
"RECORDING_FOLDER_LABEL": "录制文件夹",
"RECORDING_FOLDER_DESCRIPTION": "用于保存 Embedded MPV 直播录制的文件夹。留空则使用系统下载文件夹。",
"RECORDING_FOLDER": "录制文件夹",
"RECORDING_FOLDER_SELECT": "选择录制文件夹",
"VLC_PLAYER_PATH_LABEL": "VLC路径",
"VLC_PLAYER_PATH_DESCRIPTION": "设置 VLC 路径。在 macOS 上可以使用 VLC 应用包,例如 /Applications/VLC.app,也可以使用可执行文件路径。",
"VLC_PLAYER_PATH": "VLC播放器路径",
+6
View File
@@ -246,6 +246,8 @@
"NEW_VERSION_AVAILABLE": "有新版本可用",
"LATEST_VERSION": "您正在使用最新版本",
"STREAM_FORMAT": "串流格式",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "雙擊啟動 MPV/VLC",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "在 MPV 或 VLC 開始播放前,要求雙擊頻道列。",
"LANGUAGE": "語言",
"SETTINGS_SAVED": "成功!設定已儲存。",
"THEME": "視覺主題",
@@ -326,6 +328,10 @@
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "重複使用 MPV 實體",
"MPV_REUSE_INSTANCE_DESCRIPTION": "啟用後,相同的 MPV 視窗將重複用於播放不同串流。停用時,每個串流會在新的 MPV 視窗中開啟。",
"RECORDING_FOLDER_LABEL": "錄製資料夾",
"RECORDING_FOLDER_DESCRIPTION": "用於儲存 Embedded MPV 直播錄製的資料夾。留空則使用系統下載資料夾。",
"RECORDING_FOLDER": "錄製資料夾",
"RECORDING_FOLDER_SELECT": "選擇錄製資料夾",
"VLC_PLAYER_PATH_LABEL": "VLC 路徑",
"VLC_PLAYER_PATH_DESCRIPTION": "設定 VLC 路徑。在 macOS 上可以使用 VLC App bundle,例如 /Applications/VLC.app,也可以使用可執行檔路徑。",
"VLC_PLAYER_PATH": "VLC 播放器路徑",