mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
feat(settings): expose shared web controls toggle
This commit is contained in:
1 parent
0e49a6f3e1
commit
c33fb957ff
23 files changed
+163
-84
No files matched your search
@@ -31,6 +31,7 @@ export function createSettingsForm(
|
||||
) {
|
||||
return formBuilder.group({
|
||||
player: [VideoPlayer.VideoJs],
|
||||
webPlayerSharedControls: false,
|
||||
...(supportsEpg
|
||||
? { epgUrl: new FormArray<FormControl<string | null>>([]) }
|
||||
: {}),
|
||||
@@ -114,6 +115,7 @@ export function createSettingsFromFormValue(
|
||||
|
||||
return {
|
||||
player: value.player ?? VideoPlayer.VideoJs,
|
||||
webPlayerSharedControls: value.webPlayerSharedControls ?? false,
|
||||
streamFormat: value.streamFormat ?? StreamFormat.AutoStreamFormat,
|
||||
openStreamOnDoubleClick: value.openStreamOnDoubleClick ?? false,
|
||||
language: value.language ?? Language.ENGLISH,
|
||||
|
||||
@@ -69,6 +69,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (isWebPlayerSelected()) {
|
||||
<div
|
||||
class="setting-item"
|
||||
data-test-id="web-player-shared-controls-setting"
|
||||
>
|
||||
<div class="setting-item__meta">
|
||||
<h4>
|
||||
{{ 'SETTINGS.WEB_PLAYER_SHARED_CONTROLS' | translate }}
|
||||
</h4>
|
||||
<p>
|
||||
{{
|
||||
'SETTINGS.WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION'
|
||||
| translate
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="setting-item__control setting-item__toggle">
|
||||
<mat-checkbox
|
||||
formControlName="webPlayerSharedControls"
|
||||
data-test-id="web-player-shared-controls-toggle"
|
||||
></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (supportsManagedExternalPlayers() && isExternalPlayerSelected()) {
|
||||
<div
|
||||
class="setting-item"
|
||||
|
||||
@@ -18,6 +18,9 @@ const VLC_ARGUMENTS_PLACEHOLDER = '--video-on-top\n--width=640\n--height=360';
|
||||
describe('SettingsPlaybackSectionComponent', () => {
|
||||
let fixture: ComponentFixture<SettingsPlaybackSectionComponent>;
|
||||
|
||||
const queryByTestId = (testId: string): Element | null =>
|
||||
fixture.nativeElement.querySelector(`[data-test-id="${testId}"]`);
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@@ -65,9 +68,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="external-player-double-click-setting"]'
|
||||
)
|
||||
queryByTestId('external-player-double-click-setting')
|
||||
).toBeNull();
|
||||
expect(fixture.nativeElement.textContent).not.toContain(
|
||||
'SETTINGS.OPEN_STREAM_ON_DOUBLE_CLICK'
|
||||
@@ -80,9 +81,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="external-player-double-click-setting"]'
|
||||
)
|
||||
queryByTestId('external-player-double-click-setting')
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
@@ -98,9 +97,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="external-player-double-click-setting"]'
|
||||
)
|
||||
queryByTestId('external-player-double-click-setting')
|
||||
).not.toBeNull();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'SETTINGS.OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK'
|
||||
@@ -116,18 +113,14 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="external-player-double-click-setting"]'
|
||||
)
|
||||
queryByTestId('external-player-double-click-setting')
|
||||
).toBeNull();
|
||||
|
||||
form.controls['player'].setValue(VideoPlayer.MPV);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="external-player-double-click-setting"]'
|
||||
)
|
||||
queryByTestId('external-player-double-click-setting')
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
@@ -146,6 +139,44 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
expect(option?.textContent).toContain('auto');
|
||||
});
|
||||
|
||||
it.each([
|
||||
VideoPlayer.VideoJs,
|
||||
VideoPlayer.Html5Player,
|
||||
VideoPlayer.ArtPlayer,
|
||||
])('shows shared web controls for %s', (player) => {
|
||||
fixture.componentRef.setInput('form', createForm(player));
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
queryByTestId('web-player-shared-controls-setting')
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it.each([VideoPlayer.EmbeddedMpv, VideoPlayer.MPV, VideoPlayer.VLC])(
|
||||
'hides shared web controls for %s',
|
||||
(player) => {
|
||||
fixture.componentRef.setInput('form', createForm(player));
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
queryByTestId('web-player-shared-controls-setting')
|
||||
).toBeNull();
|
||||
}
|
||||
);
|
||||
|
||||
it('binds the shared web controls checkbox to the settings form', () => {
|
||||
const form = createForm();
|
||||
form.controls['webPlayerSharedControls'].setValue(true);
|
||||
fixture.componentRef.setInput('form', form);
|
||||
fixture.detectChanges();
|
||||
|
||||
const checkbox = fixture.nativeElement.querySelector<HTMLInputElement>(
|
||||
'[data-test-id="web-player-shared-controls-toggle"] input[type="checkbox"]'
|
||||
);
|
||||
|
||||
expect(checkbox?.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps the double-click option visible when path settings are unavailable', () => {
|
||||
fixture.componentRef.setInput('form', createForm(VideoPlayer.MPV));
|
||||
fixture.componentRef.setInput('isDesktop', true);
|
||||
@@ -157,18 +188,12 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="external-player-double-click-setting"]'
|
||||
)
|
||||
queryByTestId('external-player-double-click-setting')
|
||||
).not.toBeNull();
|
||||
expect(fixture.nativeElement.textContent).not.toContain(
|
||||
MPV_PATH_DESCRIPTION
|
||||
);
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="mpv-compatible-player-tip"]'
|
||||
)
|
||||
).toBeNull();
|
||||
expect(queryByTestId('mpv-compatible-player-tip')).toBeNull();
|
||||
});
|
||||
|
||||
it.each<[string, boolean, boolean, boolean, boolean]>([
|
||||
@@ -188,9 +213,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
const setting = fixture.nativeElement.querySelector(
|
||||
'[data-test-id="embedded-mpv-frame-copy-setting"]'
|
||||
);
|
||||
const setting = queryByTestId('embedded-mpv-frame-copy-setting');
|
||||
expect(Boolean(setting)).toBe(expected);
|
||||
}
|
||||
);
|
||||
@@ -199,20 +222,12 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
fixture.componentRef.setInput('isDesktop', true);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="recording-folder-setting"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
expect(queryByTestId('recording-folder-setting')).not.toBeNull();
|
||||
|
||||
fixture.componentRef.setInput('isDesktop', false);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="recording-folder-setting"]'
|
||||
)
|
||||
).toBeNull();
|
||||
expect(queryByTestId('recording-folder-setting')).toBeNull();
|
||||
});
|
||||
|
||||
it('shows MPV bundle guidance and the IINA executable tip for desktop MPV playback', () => {
|
||||
@@ -227,11 +242,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
MPV_PATH_DESCRIPTION
|
||||
);
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="mpv-compatible-player-tip"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
expect(queryByTestId('mpv-compatible-player-tip')).not.toBeNull();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
MPV_COMPATIBLE_PLAYER_TIP
|
||||
);
|
||||
@@ -253,11 +264,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
expect(fixture.nativeElement.textContent).not.toContain(
|
||||
MPV_COMPATIBLE_PLAYER_TIP
|
||||
);
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="mpv-compatible-player-tip"]'
|
||||
)
|
||||
).toBeNull();
|
||||
expect(queryByTestId('mpv-compatible-player-tip')).toBeNull();
|
||||
});
|
||||
|
||||
it('shows VLC bundle guidance without the IINA tip for desktop VLC playback', () => {
|
||||
@@ -272,11 +279,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
VLC_PATH_DESCRIPTION
|
||||
);
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="mpv-compatible-player-tip"]'
|
||||
)
|
||||
).toBeNull();
|
||||
expect(queryByTestId('mpv-compatible-player-tip')).toBeNull();
|
||||
expect(fixture.nativeElement.textContent).not.toContain(
|
||||
MPV_COMPATIBLE_PLAYER_TIP
|
||||
);
|
||||
@@ -295,11 +298,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
expect(fixture.nativeElement.textContent).not.toContain(
|
||||
VLC_PATH_DESCRIPTION
|
||||
);
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="mpv-compatible-player-tip"]'
|
||||
)
|
||||
).toBeNull();
|
||||
expect(queryByTestId('mpv-compatible-player-tip')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not show external-player path guidance for embedded players', () => {
|
||||
@@ -316,11 +315,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
expect(fixture.nativeElement.textContent).not.toContain(
|
||||
VLC_PATH_DESCRIPTION
|
||||
);
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="mpv-compatible-player-tip"]'
|
||||
)
|
||||
).toBeNull();
|
||||
expect(queryByTestId('mpv-compatible-player-tip')).toBeNull();
|
||||
});
|
||||
|
||||
it('shows MPV command-line arguments only when MPV is selected', () => {
|
||||
@@ -332,16 +327,8 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
);
|
||||
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(queryByTestId('mpv-player-arguments-setting')).not.toBeNull();
|
||||
expect(queryByTestId('vlc-player-arguments-setting')).toBeNull();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'SETTINGS.MPV_PLAYER_ARGUMENTS_LABEL'
|
||||
);
|
||||
@@ -361,16 +348,8 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
);
|
||||
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(queryByTestId('vlc-player-arguments-setting')).not.toBeNull();
|
||||
expect(queryByTestId('mpv-player-arguments-setting')).toBeNull();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'SETTINGS.VLC_PLAYER_ARGUMENTS_LABEL'
|
||||
);
|
||||
@@ -385,6 +364,7 @@ describe('SettingsPlaybackSectionComponent', () => {
|
||||
function createForm(player = VideoPlayer.VideoJs): FormGroup {
|
||||
return new FormGroup({
|
||||
player: new FormControl(player),
|
||||
webPlayerSharedControls: new FormControl(false),
|
||||
streamFormat: new FormControl(StreamFormat.AutoStreamFormat),
|
||||
openStreamOnDoubleClick: new FormControl(false),
|
||||
showExternalPlaybackBar: new FormControl(true),
|
||||
|
||||
@@ -53,6 +53,15 @@ export class SettingsPlaybackSectionComponent {
|
||||
readonly supportsExternalPlayerPathSettings = input(false);
|
||||
readonly selectRecordingFolder = output<void>();
|
||||
|
||||
isWebPlayerSelected(): boolean {
|
||||
const player = this.form().value.player;
|
||||
return (
|
||||
player === VideoPlayer.VideoJs ||
|
||||
player === VideoPlayer.Html5Player ||
|
||||
player === VideoPlayer.ArtPlayer
|
||||
);
|
||||
}
|
||||
|
||||
isExternalPlayerSelected(): boolean {
|
||||
const player = this.form().value.player;
|
||||
return player === VideoPlayer.MPV || player === VideoPlayer.VLC;
|
||||
|
||||
@@ -84,6 +84,7 @@ export class MockRouter {
|
||||
|
||||
const DEFAULT_SETTINGS = {
|
||||
player: VideoPlayer.VideoJs,
|
||||
webPlayerSharedControls: false,
|
||||
streamFormat: StreamFormat.AutoStreamFormat,
|
||||
openStreamOnDoubleClick: false,
|
||||
language: Language.ENGLISH,
|
||||
@@ -661,6 +662,19 @@ describe('SettingsComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('hydrates the shared web controls setting from the settings store', () => {
|
||||
const mockStore = settingsStore as unknown as MockSettingsStore;
|
||||
mockStore._setSettings({
|
||||
webPlayerSharedControls: true,
|
||||
});
|
||||
|
||||
component.setSettings();
|
||||
|
||||
expect(
|
||||
component.settingsForm.get('webPlayerSharedControls')?.value
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('hides the embedded mpv option when the desktop support probe reports unsupported', async () => {
|
||||
window.electron = {
|
||||
...window.electron,
|
||||
@@ -1301,9 +1315,7 @@ describe('SettingsComponent', () => {
|
||||
|
||||
it('updates the EPG view mode through the epg section output', () => {
|
||||
const mockStore = settingsStore as unknown as MockSettingsStore;
|
||||
const listButton = (
|
||||
fixture.nativeElement as HTMLElement
|
||||
).querySelector(
|
||||
const listButton = (fixture.nativeElement as HTMLElement).querySelector(
|
||||
'[data-test-id="epg-view-mode-list"]'
|
||||
) as HTMLButtonElement;
|
||||
|
||||
@@ -1398,6 +1410,21 @@ describe('SettingsComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('saves the shared web controls setting on submit', async () => {
|
||||
const mockStore = settingsStore as unknown as MockSettingsStore;
|
||||
mockStore.updateSettings.mockResolvedValue(undefined);
|
||||
component.settingsForm.get('webPlayerSharedControls')?.setValue(true);
|
||||
|
||||
component.onSubmit();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(mockStore.updateSettings).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
webPlayerSharedControls: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('clears external player paths in Electron when saved as empty', async () => {
|
||||
const mockStore = settingsStore as unknown as MockSettingsStore;
|
||||
mockStore.updateSettings.mockResolvedValue(undefined);
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "جارٍ المسح…",
|
||||
"EPG_URL_ERROR": "القيمة ليست رابطًا صالحًا",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "اختر مشغل الفيديو الافتراضي",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "عناصر تحكم موحّدة لمشغلات الويب (تجريبي)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "استخدم عناصر التحكم المشتركة في IPTVnator مع HTML5 وVideo.js وArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "اختر تنسيق البث الافتراضي (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "اختر لغة التطبيق",
|
||||
"THEME_DESCRIPTION": "اختر السمة البصرية للتطبيق",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "جاري المسح…",
|
||||
"EPG_URL_ERROR": "القيمة ماشي رابط صحيح",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "اختار مشغل الفيديو الافتراضي",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "عناصر تحكم موحدة لمشغلات الويب (تجريبية)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "استعمل عناصر التحكم المشتركة ديال IPTVnator مع HTML5 وVideo.js وArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "اختار صيغة البث الافتراضية (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "اختار لغة التطبيق",
|
||||
"THEME_DESCRIPTION": "اختار المظهر المرئي للتطبيق",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Ачышчаецца...",
|
||||
"EPG_URL_ERROR": "Уведзенае значэнне не з'яўляецца URL-адрасам.",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Выбраць стандартны відэапрайгравальнік",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Адзіныя элементы кіравання для вэб-прайгравальнікаў (эксперыментальна)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Выкарыстоўваць агульныя элементы кіравання IPTVnator у HTML5, Video.js і ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Выберыце фармат патоку па змаўчанні (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Выбраць мову праграмы",
|
||||
"THEME_DESCRIPTION": "Выбраць візуальную тэму афармлення",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Wird gelöscht…",
|
||||
"EPG_URL_ERROR": "Der Wert ist keine gültige URL",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Wählen Sie den Standard-Videoplayer aus",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Einheitliche Steuerung für Web-Player (experimentell)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Verwendet die gemeinsamen IPTVnator-Steuerelemente in HTML5, Video.js und ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Standard-Streamformat auswählen (Xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Wählen Sie die Sprache der Anwendung aus",
|
||||
"THEME_DESCRIPTION": "Wählen Sie das visuelle Thema der Anwendung aus",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Διαγραφή…",
|
||||
"EPG_URL_ERROR": "Η τιμή δεν είναι έγκυρη διεύθυνση URL",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Επιλογή προεπιλεγμένου προγράμματος αναπαραγωγής βίντεο",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Ενοποιημένα στοιχεία ελέγχου για web players (πειραματικό)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Χρησιμοποιήστε τα κοινά στοιχεία ελέγχου του IPTVnator σε HTML5, Video.js και ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Επιλογή προεπιλεγμένης μορφής ροής (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Επιλέξτε τη γλώσσα της εφαρμογής",
|
||||
"THEME_DESCRIPTION": "Επιλέξτε οπτικό θέμα της εφαρμογής",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Clearing…",
|
||||
"EPG_URL_ERROR": "The value is not a valid URL",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Select default video player",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Unified controls for web players (experimental)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Use IPTVnator's shared controls in HTML5, Video.js, and ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Select default stream format (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Select language of the application",
|
||||
"THEME_DESCRIPTION": "Select visual theme of the application",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Borrando…",
|
||||
"EPG_URL_ERROR": "El valor no es una URL válida.",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Seleccionar reproductor de vídeo predeterminado",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Controles unificados para reproductores web (experimental)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Usa los controles compartidos de IPTVnator en HTML5, Video.js y ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Selecciona el formato de transmision predeterminado (Xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Seleccione el idioma de la aplicación",
|
||||
"THEME_DESCRIPTION": "Seleccione el tema visual de la aplicación.",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Effacement…",
|
||||
"EPG_URL_ERROR": "La valeur n'est pas une URL valide",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Sélectionnez le lecteur vidéo par défaut",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Commandes unifiées pour les lecteurs web (expérimental)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Utilise les commandes partagées d’IPTVnator dans HTML5, Video.js et ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Sélectionnez le format de flux par défaut (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Sélectionnez la langue de l'application",
|
||||
"THEME_DESCRIPTION": "Sélectionnez le thème visuel de l'application",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Cancellazione…",
|
||||
"EPG_URL_ERROR": "Il valore non è una URL valida",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Seleziona lettore video predefinito",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Controlli unificati per i player web (sperimentale)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Usa i controlli condivisi di IPTVnator in HTML5, Video.js e ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Seleziona il formato di streaming predefinito (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Seleziona la lingua dell'applicazione",
|
||||
"THEME_DESCRIPTION": "Seleziona il tema visivo dell'applicazione",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "クリア中…",
|
||||
"EPG_URL_ERROR": "有効なURLではありません",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "デフォルトのビデオプレーヤーを選択",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Webプレーヤーの統一コントロール(試験的)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "HTML5、Video.js、ArtPlayerでIPTVnator共通のコントロールを使用します。",
|
||||
"STREAM_FORMAT_DESCRIPTION": "デフォルトのストリーム形式を選択(Xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "アプリケーションの言語を選択",
|
||||
"THEME_DESCRIPTION": "アプリケーションの視覚テーマを選択",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "지우는 중…",
|
||||
"EPG_URL_ERROR": "값이 유효한 URL이 아닙니다.",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "기본 비디오 플레이어 선택",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "웹 플레이어 통합 컨트롤(실험적)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "HTML5, Video.js 및 ArtPlayer에서 IPTVnator 공통 컨트롤을 사용합니다.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "기본 스트림 형식 선택 (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "애플리케이션 언어 선택",
|
||||
"THEME_DESCRIPTION": "애플리케이션의 시각적 테마 선택",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Wissen…",
|
||||
"EPG_URL_ERROR": "De opgegeven waarde is geen geldige URL.",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Selecteer de standaard videospeler",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Uniforme bediening voor webspelers (experimenteel)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Gebruik de gedeelde IPTVnator-bediening in HTML5, Video.js en ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Selecteer het standaard streamformaat (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Selecteer de taal van de applicatie",
|
||||
"THEME_DESCRIPTION": "Kies het visuele thema van de applicatie",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Czyszczenie…",
|
||||
"EPG_URL_ERROR": "Wartość nie jest prawidłowym URL",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Wybierz domyślny odtwarzacz wideo",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Ujednolicone sterowanie odtwarzaczami internetowymi (eksperymentalne)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Używaj wspólnych elementów sterowania IPTVnator w HTML5, Video.js i ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Wybierz domyślny format strumienia (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Wybierz język aplikacji",
|
||||
"THEME_DESCRIPTION": "Wybierz motyw wizualny aplikacji",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Limpando…",
|
||||
"EPG_URL_ERROR": "O valor não é uma URL válida",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Selecionar reprodutor de vídeo padrão",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Controlos unificados para leitores web (experimental)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Usa os controlos partilhados do IPTVnator em HTML5, Video.js e ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Selecionar formato de stream padrão (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Selecionar idioma do aplicativo",
|
||||
"THEME_DESCRIPTION": "Selecionar tema visual do aplicativo",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Очистка…",
|
||||
"EPG_URL_ERROR": "Введенное значение не является URL-адресом.",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Выбрать стандартный видео плеер",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Единые элементы управления для веб-плееров (экспериментально)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Использовать общие элементы управления IPTVnator в HTML5, Video.js и ArtPlayer.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Выбрать формат потока по умолчанию (Xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Выбрать язык приложения",
|
||||
"THEME_DESCRIPTION": "Выбрать визуальную тему оформления",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "Temizleniyor…",
|
||||
"EPG_URL_ERROR": "Girilen değer geçerli bir URL değildir",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Varsayılan video oynatıcıyı seçin",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Web oynatıcıları için birleşik kontroller (deneysel)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "HTML5, Video.js ve ArtPlayer'da IPTVnator'ın ortak kontrollerini kullan.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Varsayılan yayın formatını seçin (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Uygulamanın dilini seçin",
|
||||
"THEME_DESCRIPTION": "Uygulamanın görünüm temasını seçin",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "清除中…",
|
||||
"EPG_URL_ERROR": "该值不是有效的 URL",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "选择默认视频播放器",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Web 播放器统一控件(实验性)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "在 HTML5、Video.js 和 ArtPlayer 中使用 IPTVnator 的共享控件。",
|
||||
"STREAM_FORMAT_DESCRIPTION": "选择默认的流格式(xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "选择应用程序的语言",
|
||||
"THEME_DESCRIPTION": "选择应用程序的视觉主题",
|
||||
|
||||
@@ -296,6 +296,8 @@
|
||||
"CLEARING_EPG_DATA": "正在清除…",
|
||||
"EPG_URL_ERROR": "該值不是有效的網址",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "選擇預設影片播放器",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Web 播放器統一控制項(實驗性)",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "在 HTML5、Video.js 和 ArtPlayer 中使用 IPTVnator 的共用控制項。",
|
||||
"STREAM_FORMAT_DESCRIPTION": "選擇預設串流格式(xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "選擇應用程式語言",
|
||||
"THEME_DESCRIPTION": "選擇應用程式視覺主題",
|
||||
|
||||
Reference in new issue
Block a user