diff --git a/apps/web/src/app/settings/settings-form.utils.ts b/apps/web/src/app/settings/settings-form.utils.ts index da7d20cef..f09c4aa6e 100644 --- a/apps/web/src/app/settings/settings-form.utils.ts +++ b/apps/web/src/app/settings/settings-form.utils.ts @@ -31,6 +31,7 @@ export function createSettingsForm( ) { return formBuilder.group({ player: [VideoPlayer.VideoJs], + webPlayerSharedControls: false, ...(supportsEpg ? { epgUrl: new FormArray>([]) } : {}), @@ -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, 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 defb115c5..15510dcb3 100644 --- a/apps/web/src/app/settings/settings-playback-section.component.html +++ b/apps/web/src/app/settings/settings-playback-section.component.html @@ -69,6 +69,31 @@ + @if (isWebPlayerSelected()) { +
+
+

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

+

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

+
+
+ +
+
+ } + @if (supportsManagedExternalPlayers() && isExternalPlayerSelected()) {
{ let fixture: ComponentFixture; + 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( + '[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), 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 5e8f645b7..722faca52 100644 --- a/apps/web/src/app/settings/settings-playback-section.component.ts +++ b/apps/web/src/app/settings/settings-playback-section.component.ts @@ -53,6 +53,15 @@ export class SettingsPlaybackSectionComponent { readonly supportsExternalPlayerPathSettings = input(false); readonly selectRecordingFolder = output(); + 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; diff --git a/apps/web/src/app/settings/settings.component.spec.ts b/apps/web/src/app/settings/settings.component.spec.ts index 95ffca533..d4ac9ac93 100644 --- a/apps/web/src/app/settings/settings.component.spec.ts +++ b/apps/web/src/app/settings/settings.component.spec.ts @@ -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); diff --git a/apps/web/src/assets/i18n/ar.json b/apps/web/src/assets/i18n/ar.json index c2b746dc0..81cb8bfec 100644 --- a/apps/web/src/assets/i18n/ar.json +++ b/apps/web/src/assets/i18n/ar.json @@ -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": "اختر السمة البصرية للتطبيق", diff --git a/apps/web/src/assets/i18n/ary.json b/apps/web/src/assets/i18n/ary.json index d4c8f9aed..8615f456b 100644 --- a/apps/web/src/assets/i18n/ary.json +++ b/apps/web/src/assets/i18n/ary.json @@ -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": "اختار المظهر المرئي للتطبيق", diff --git a/apps/web/src/assets/i18n/by.json b/apps/web/src/assets/i18n/by.json index ce6157a0c..3c3f62f7f 100644 --- a/apps/web/src/assets/i18n/by.json +++ b/apps/web/src/assets/i18n/by.json @@ -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": "Выбраць візуальную тэму афармлення", diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json index 9b81484d6..4b311c69f 100644 --- a/apps/web/src/assets/i18n/de.json +++ b/apps/web/src/assets/i18n/de.json @@ -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", diff --git a/apps/web/src/assets/i18n/el.json b/apps/web/src/assets/i18n/el.json index 9ab7b28cc..c65264fa3 100644 --- a/apps/web/src/assets/i18n/el.json +++ b/apps/web/src/assets/i18n/el.json @@ -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": "Επιλέξτε οπτικό θέμα της εφαρμογής", diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index 0ae94e68d..dd305961d 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -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", diff --git a/apps/web/src/assets/i18n/es.json b/apps/web/src/assets/i18n/es.json index 695aa7304..12395cd45 100644 --- a/apps/web/src/assets/i18n/es.json +++ b/apps/web/src/assets/i18n/es.json @@ -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.", diff --git a/apps/web/src/assets/i18n/fr.json b/apps/web/src/assets/i18n/fr.json index 328efd3c3..769382a0c 100644 --- a/apps/web/src/assets/i18n/fr.json +++ b/apps/web/src/assets/i18n/fr.json @@ -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", diff --git a/apps/web/src/assets/i18n/it.json b/apps/web/src/assets/i18n/it.json index fe89e649a..becf1856a 100644 --- a/apps/web/src/assets/i18n/it.json +++ b/apps/web/src/assets/i18n/it.json @@ -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", diff --git a/apps/web/src/assets/i18n/ja.json b/apps/web/src/assets/i18n/ja.json index 526e50d78..30277f87f 100644 --- a/apps/web/src/assets/i18n/ja.json +++ b/apps/web/src/assets/i18n/ja.json @@ -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": "アプリケーションの視覚テーマを選択", diff --git a/apps/web/src/assets/i18n/ko.json b/apps/web/src/assets/i18n/ko.json index 9c4896445..27e08f9fe 100644 --- a/apps/web/src/assets/i18n/ko.json +++ b/apps/web/src/assets/i18n/ko.json @@ -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": "애플리케이션의 시각적 테마 선택", diff --git a/apps/web/src/assets/i18n/nl.json b/apps/web/src/assets/i18n/nl.json index 6f59ef527..ccf3ec726 100644 --- a/apps/web/src/assets/i18n/nl.json +++ b/apps/web/src/assets/i18n/nl.json @@ -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", diff --git a/apps/web/src/assets/i18n/pl.json b/apps/web/src/assets/i18n/pl.json index fc3a6f2dd..0be167258 100644 --- a/apps/web/src/assets/i18n/pl.json +++ b/apps/web/src/assets/i18n/pl.json @@ -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", diff --git a/apps/web/src/assets/i18n/pt.json b/apps/web/src/assets/i18n/pt.json index 56cae6e28..51ecc82f8 100644 --- a/apps/web/src/assets/i18n/pt.json +++ b/apps/web/src/assets/i18n/pt.json @@ -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", diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json index cf9809bbf..1fa420759 100644 --- a/apps/web/src/assets/i18n/ru.json +++ b/apps/web/src/assets/i18n/ru.json @@ -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": "Выбрать визуальную тему оформления", diff --git a/apps/web/src/assets/i18n/tr.json b/apps/web/src/assets/i18n/tr.json index 446aa5c17..74a629255 100644 --- a/apps/web/src/assets/i18n/tr.json +++ b/apps/web/src/assets/i18n/tr.json @@ -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", diff --git a/apps/web/src/assets/i18n/zh.json b/apps/web/src/assets/i18n/zh.json index 2b5fa958f..03fa17ac3 100644 --- a/apps/web/src/assets/i18n/zh.json +++ b/apps/web/src/assets/i18n/zh.json @@ -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": "选择应用程序的视觉主题", diff --git a/apps/web/src/assets/i18n/zhtw.json b/apps/web/src/assets/i18n/zhtw.json index 0e122c3fb..97c7004c9 100644 --- a/apps/web/src/assets/i18n/zhtw.json +++ b/apps/web/src/assets/i18n/zhtw.json @@ -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": "選擇應用程式視覺主題",