From 4d2e17565d247c2a6bc9ae3d23ab37ff52033478 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sun, 5 Sep 2021 16:39:56 +0200 Subject: [PATCH] feat: global subtitle display setting You can now activate/deactivate the visiblity of subtitles on a global application level. --- .../html-video-player.component.ts | 19 ++ .../video-player/video-player.component.html | 6 +- .../video-player.component.spec.ts | 6 +- .../video-player/video-player.component.ts | 17 +- .../vjs-player/vjs-player.component.scss | 9 + src/app/settings/settings.component.html | 10 + src/app/settings/settings.component.spec.ts | 2 + src/app/settings/settings.component.ts | 16 +- src/app/settings/settings.interface.ts | 8 +- src/assets/i18n/de.json | 5 +- src/assets/i18n/en.json | 3 +- src/assets/i18n/es.json | 3 +- src/assets/i18n/ko.json | 3 +- src/assets/i18n/ru.json | 3 +- src/assets/i18n/zh.json | 243 +++++++++--------- 15 files changed, 209 insertions(+), 144 deletions(-) diff --git a/src/app/player/components/html-video-player/html-video-player.component.ts b/src/app/player/components/html-video-player/html-video-player.component.ts index eba6273f9..36532724e 100644 --- a/src/app/player/components/html-video-player/html-video-player.component.ts +++ b/src/app/player/components/html-video-player/html-video-player.component.ts @@ -26,6 +26,9 @@ export class HtmlVideoPlayerComponent implements OnChanges, OnDestroy { /** HLS object */ hls: Hls; + /** Captions/subtitles indicator */ + @Input() showCaptions!: boolean; + /** * Listen for component input changes * @param changes component changes @@ -54,6 +57,19 @@ export class HtmlVideoPlayerComponent implements OnChanges, OnDestroy { } } + /** + * Disables text based captions based on the global settings + */ + disableCaptions(): void { + for ( + let i = 0; + i < this.videoPlayer.nativeElement.textTracks.length; + i++ + ) { + this.videoPlayer.nativeElement.textTracks[i].mode = 'hidden'; + } + } + /** * Handles promise based play operation */ @@ -64,6 +80,9 @@ export class HtmlVideoPlayerComponent implements OnChanges, OnDestroy { playPromise .then((_) => { // Automatic playback started! + if (!this.showCaptions) { + this.disableCaptions(); + } }) .catch((error) => {}); } diff --git a/src/app/player/components/video-player/video-player.component.html b/src/app/player/components/video-player/video-player.component.html index f3940f120..80c2c96c6 100644 --- a/src/app/player/components/video-player/video-player.component.html +++ b/src/app/player/components/video-player/video-player.component.html @@ -51,7 +51,7 @@ diff --git a/src/app/player/components/video-player/video-player.component.spec.ts b/src/app/player/components/video-player/video-player.component.spec.ts index 055228b7f..4d2d69153 100644 --- a/src/app/player/components/video-player/video-player.component.spec.ts +++ b/src/app/player/components/video-player/video-player.component.spec.ts @@ -16,6 +16,7 @@ import * as MOCKED_PLAYLIST from '../../../../mocks/playlist.json'; import { createChannel } from '../../../state'; import { HtmlVideoPlayerComponent } from '../html-video-player/html-video-player.component'; import { EpgListComponent } from '../epg-list/epg-list.component'; +import { VideoPlayer } from '../../../settings/settings.interface'; class MatSnackBarStub { open(): void {} @@ -73,7 +74,10 @@ describe('VideoPlayerComponent', () => { it('should check default component settings', () => { fixture.detectChanges(); - expect(component.player).toEqual('videojs'); + expect(component.playerSettings).toEqual({ + player: VideoPlayer.VideoJs, + showCaptions: false, + }); }); it('should update store after channel was faved', () => { diff --git a/src/app/player/components/video-player/video-player.component.ts b/src/app/player/components/video-player/video-player.component.ts index 2c793b25c..45dcc249b 100644 --- a/src/app/player/components/video-player/video-player.component.ts +++ b/src/app/player/components/video-player/video-player.component.ts @@ -5,10 +5,7 @@ import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { MatSidenav } from '@angular/material/sidenav'; import { StorageMap } from '@ngx-pwa/local-storage'; -import { - Settings, - VideoPlayerType, -} from '../../../settings/settings.interface'; +import { Settings, VideoPlayer } from '../../../settings/settings.interface'; import { MatSnackBar } from '@angular/material/snack-bar'; /** Settings key in storage */ @@ -46,8 +43,11 @@ export class VideoPlayerComponent implements OnInit { (store) => store.favorites ); - /** Selected video player component */ - player: VideoPlayerType = 'videojs'; + /** Selected video player options */ + playerSettings: Partial = { + player: VideoPlayer.VideoJs, + showCaptions: false, + }; /** Sidebar object */ @ViewChild('sidenav') sideNav: MatSidenav; @@ -79,7 +79,10 @@ export class VideoPlayerComponent implements OnInit { applySettings(): void { this.storage.get(SETTINGS_STORE_KEY).subscribe((settings: Settings) => { if (settings && Object.keys(settings).length > 0) { - this.player = settings.player; + this.playerSettings = { + player: settings.player, + showCaptions: settings.showCaptions, + }; } }); } diff --git a/src/app/player/components/vjs-player/vjs-player.component.scss b/src/app/player/components/vjs-player/vjs-player.component.scss index d34c066ac..ebe555303 100644 --- a/src/app/player/components/vjs-player/vjs-player.component.scss +++ b/src/app/player/components/vjs-player/vjs-player.component.scss @@ -5,3 +5,12 @@ width: 100% !important; height: calc(100vh - 85px); } + +/* Hide the captions settings item from the captions menu. */ +.hide-captions { + .vjs-texttrack-settings, + .vjs-text-track-display, + .vjs-subs-caps-button { + display: none; + } +} diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index 3e456a49f..88de339c7 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -119,6 +119,16 @@

+ +
+

{{ 'SETTINGS.SHOW_CAPTIONS' | translate }}

+

+ +

+
+

{{ 'SETTINGS.VERSION' | translate }}

diff --git a/src/app/settings/settings.component.spec.ts b/src/app/settings/settings.component.spec.ts index 5433d911c..ed3f74243 100644 --- a/src/app/settings/settings.component.spec.ts +++ b/src/app/settings/settings.component.spec.ts @@ -1,3 +1,4 @@ +import { MatCheckboxModule } from '@angular/material/checkbox'; import { StorageMap } from '@ngx-pwa/local-storage'; import { FormBuilder } from '@angular/forms'; /* eslint-disable @typescript-eslint/unbound-method */ @@ -72,6 +73,7 @@ describe('SettingsComponent', () => { MockModule(MatCardModule), MockModule(MatListModule), MockModule(MatFormFieldModule), + MockModule(MatCheckboxModule), ], }).compileComponents(); }) diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 1b95cce35..08cd6be29 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -5,7 +5,7 @@ import { StorageMap } from '@ngx-pwa/local-storage'; import { Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Observable, Subscription } from 'rxjs'; -import { Settings } from './settings.interface'; +import { Settings, VideoPlayer } from './settings.interface'; import { HttpClient } from '@angular/common/http'; import * as semver from 'semver'; import { ElectronService } from '../services/electron.service'; @@ -34,11 +34,11 @@ export class SettingsComponent implements OnInit, OnDestroy { /** Player options */ players = [ { - id: 'html5', + id: VideoPlayer.Html5Player, label: 'HTML5 Video Player', }, { - id: 'videojs', + id: VideoPlayer.VideoJs, label: 'VideoJs Player', }, ]; @@ -84,9 +84,10 @@ export class SettingsComponent implements OnInit, OnDestroy { private translate: TranslateService ) { this.settingsForm = this.formBuilder.group({ - player: ['videojs'], + player: [VideoPlayer.VideoJs], epgUrl: '', language: Language.ENGLISH, + showCaptions: false, theme: Theme.LightTheme, }); @@ -123,11 +124,16 @@ export class SettingsComponent implements OnInit, OnDestroy { this.storage.get('settings').subscribe((settings: Settings) => { if (settings) { this.settingsForm.setValue({ - player: settings.player ? settings.player : 'videojs', + player: settings.player + ? settings.player + : VideoPlayer.VideoJs, epgUrl: settings.epgUrl ? settings.epgUrl : '', language: settings.language ? settings.language : Language.ENGLISH, + showCaptions: settings.showCaptions + ? settings.showCaptions + : false, theme: settings.theme ? settings.theme : Theme.LightTheme, diff --git a/src/app/settings/settings.interface.ts b/src/app/settings/settings.interface.ts index 5a3aa03df..43648adc2 100644 --- a/src/app/settings/settings.interface.ts +++ b/src/app/settings/settings.interface.ts @@ -1,14 +1,18 @@ import { Language } from './language.enum'; import { Theme } from './theme.enum'; -export type VideoPlayerType = 'html5' | 'videojs'; +export enum VideoPlayer { + VideoJs = 'videojs', + Html5Player = 'html5', +} /** * Describes all available settings options of the application */ export interface Settings { - player: VideoPlayerType; + player: VideoPlayer; epgUrl: string; language: Language; + showCaptions: boolean; theme: Theme; } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index fb1735a51..ddc45f0e8 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -24,7 +24,7 @@ "PROGRAM_DIALOG": { "PROGRAM_DETAILS": "Details zum TV-Programm", "TITLE": "Titel", - "CATEGORY": "Ketegorie", + "CATEGORY": "Kategorie", "PARENTAL_RATING_SYSTEM": "Alterskennzeichnung (USK)", "DESCRIPTION": "Beschreibung", "LANGUAGE": "Sprache", @@ -105,7 +105,8 @@ "VIDEO_PLAYER_LABEL": "Video player", "VIDEO_PLAYER_PLACEHOLDER": "Option auswählen", "SETTINGS_SAVED": "Einstellungen wurden erfolgreich gespeichert!", - "THEME": "Farbdesign" + "THEME": "Farbdesign", + "SHOW_CAPTIONS": "Untertitel anzeigen" }, "THEMES": { "DARK_THEME": "Dunkel", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index de12becc4..50ca5d4d8 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -65,7 +65,8 @@ "LATEST_VERSION": "You are using the latest version", "LANGUAGE": "Language", "SETTINGS_SAVED": "Success! Configuration was saved.", - "THEME": "Visual theme" + "THEME": "Visual theme", + "SHOW_CAPTIONS": "Show subtitles" }, "THEMES": { "DARK_THEME": "Dark theme", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 83ca0ea22..40b70fdbd 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -65,7 +65,8 @@ "LATEST_VERSION": "Estás usando la versión reciente", "LANGUAGE": "Idioma", "SETTINGS_SAVED": "¡Completado! La configuración se guardó.", - "THEME": "Tema visual" + "THEME": "Tema visual", + "SHOW_CAPTIONS": "Mostrar subtítulos" }, "THEMES": { "DARK_THEME": "Tema oscuro", diff --git a/src/assets/i18n/ko.json b/src/assets/i18n/ko.json index 154fad3f8..d930b628b 100644 --- a/src/assets/i18n/ko.json +++ b/src/assets/i18n/ko.json @@ -65,7 +65,8 @@ "LATEST_VERSION": "최신 버전을 사용하고 있습니다", "LANGUAGE": "언어", "SETTINGS_SAVED": "설정이 저장되었습니다.", - "THEME": "테마" + "THEME": "테마", + "SHOW_CAPTIONS": "Show subtitles" }, "THEMES": { "DARK_THEME": "어두운 테마", diff --git a/src/assets/i18n/ru.json b/src/assets/i18n/ru.json index 34b4ec9bf..4ba6175f1 100644 --- a/src/assets/i18n/ru.json +++ b/src/assets/i18n/ru.json @@ -65,7 +65,8 @@ "LATEST_VERSION": "Вы используете последнюю версию приложения", "LANGUAGE": "Язык", "SETTINGS_SAVED": "Новые настройки были успешно сохранены!", - "THEME": "Тема оформления" + "THEME": "Тема оформления", + "SHOW_CAPTIONS": "Отображать субтитры" }, "THEMES": { "DARK_THEME": "Тёмная тема", diff --git a/src/assets/i18n/zh.json b/src/assets/i18n/zh.json index 8e0c38be3..4e0e8f8e9 100644 --- a/src/assets/i18n/zh.json +++ b/src/assets/i18n/zh.json @@ -1,121 +1,122 @@ -{ - "HOME": { - "TITLE": "IPTVnator", - "SUBTITLE": "选择一个播放列表或者从URL/文件中添加一个新的播放列表", - "TABS": { - "RECENTLY_ADDED": "最近添加的播放列表", - "FILE_UPLOAD": "从文件添加", - "URL_UPLOAD": "从URL链接添加" - }, - "PLAYLISTS": { - "NO_PLAYLISTS": "未添加播放列表", - "ADD_FIRST": "从其他标签页添加你的第一个播放列表", - "ADDED_VIA_URL": "添加自URL链接:", - "ADDED_VIA_FILE": "添加自文件", - "CHANNELS": "频道", - "ADDED": "已添加的", - "SHOW_DETAILS": "显示播放列表详情", - "REMOVE": "移除播放列表", - "INFO_DIALOG": { - "PLAYLIST_DETAILS": "播放列表详情", - "TITLE": "标题", - "FROM_URL": "添加自URL链接", - "IMPORT_DATE": "导入日期", - "CHANNELS": "频道数", - "CLOSE": "关闭", - "SAVE": "保存", - "USER_AGENT": "User agent", - "ORIGINAL_FILENAME": "原始文件名", - "FILE_PATH": "文件路径", - "UPDATE_FAILED": "上次播放列表更新失败", - "AUTO_UPDATE": "自动更新", - "AUTO_UPDATE_DESCRIPTION": "如果启用了自动刷新功能,则每次启动应用程序时都会自动更新播放列表。", - "CUSTOM_USER_AGENT": "一些 IPTV 提供商需要特定的用户代理才能播放他们的播放列表。" - }, - "REMOVE_DIALOG": { - "TITLE": "移除播放列表", - "MESSAGE": "您确定要完全删除此播放列表吗?" - }, - "UPDATED": "更新", - "REFRESH": "刷新播放列表" - }, - "FILE_UPLOAD": { - "DRAG_DROP": "将文件拖放到此处", - "OR": "或", - "CHOOSE_PLAYLIST": "选择一个播放列表" - }, - "URL_UPLOAD": { - "PLAYLIST_URL": "播放一个播放列表(m3u, m3u8)", - "ADD_PLAYLIST": "添加播放列表" - } - }, - "SETTINGS": { - "TITLE": "设置 / IPTVnator", - "SUBTITLE": "更改应用程序的配置 ", - "DESCRIPTION": "请调整以下列表中的选项并使用保存按钮确认", - "EPG_URL_LABEL": "电子节目单EPG Url (*.xml 或 *.xml.gz)", - "EPG_URL_PLACEHOLDER": "输入一个 URL", - "REFRESH_EPG": "刷新电子节目单EPG", - "VIDEO_PLAYER_LABEL": "视频播放器", - "VIDEO_PLAYER_PLACEHOLDER": "选择一个选项", - "VERSION": "版本", - "SAVE_CHANGES": "保存更改", - "BACK_TO_HOME": "回到主页", - "NEW_VERSION_AVAILABLE": "有新版本可用 ", - "LATEST_VERSION": "您使用的是最新版本", - "LANGUAGE": "语言", - "SETTINGS_SAVED": "配置已成功保存", - "THEME": "主题" - }, - "THEMES": { - "DARK_THEME": "暗黑模式", - "LIGHT_THEME": "明亮模式" - }, - "CHANNELS": { - "SEARCH_CHANNEL": "搜索频道", - "UPLOAD_OR_SELECT_OTHER_PLAYLIST": "上传或选择其他播放列表", - "ALL_CHANNELS": "所有频道", - "GROUPS": "分组", - "UNGROUPED": "未分组", - "UNNAMED_CHANNEL": "未命名频道", - "FAVORITES": "收藏", - "NO_FAVORITES": "还没有收藏夹", - "USE_STAR_TO_FAVORITE": "使用“☆”图标将频道添加到此列表中", - "REMOVE_FAVORITE": "从收藏夹中删除" - }, - "TOP_MENU": { - "OPEN_CHANNELS_LIST": "打开频道列表", - "TOGGLE_FAVORITE_FLAG": "切换收藏夹标志", - "OPEN_EPG_LIST": "打开电子节目单EPG列表" - }, - "EPG": { - "NEXT_DAY": "明天", - "PREVIOUS_DAY": "昨天", - "LIVE_NOW": "Live now", - "LIVE_STREAM": "Live stream", - "TIMESHIFT_AVAILABLE": "时移功能可用,点击播放", - "EPG_NOT_AVAILABLE_DATE": "错误:电子节目单EPG在所选日期不可用", - "EPG_NOT_AVAILABLE_CHANNEL_TITLE": "错误:电子节目单EPG不适用于此频道", - "EPG_NOT_AVAILABLE_CHANNEL_DESCRIPTION": "请添加/更改设置项中电子节目单的URL", - "FETCH_EPG": "获取 EPG 数据...", - "PROGRAM_DIALOG": { - "PROGRAM_DETAILS": "电视节目详情", - "TITLE": "标题", - "CATEGORY": "类别", - "PARENTAL_RATING_SYSTEM": "电视内容分级系统", - "DESCRIPTION": "描述", - "LANGUAGE": "语言", - "SHOW_PROGRAM_DETAILS": "显示有关此程序的详细信息" - } - }, - "LANGUAGES": { - "CHINESE": "简体中文", - "ENGLISH": "English", - "KOREAN": "한국어", - "RUSSIAN": "Русский", - "GERMAN": "Deutsch", - "SPANISH": "Español" - }, - "YES": "是", - "NO": "否" -} +{ + "HOME": { + "TITLE": "IPTVnator", + "SUBTITLE": "选择一个播放列表或者从URL/文件中添加一个新的播放列表", + "TABS": { + "RECENTLY_ADDED": "最近添加的播放列表", + "FILE_UPLOAD": "从文件添加", + "URL_UPLOAD": "从URL链接添加" + }, + "PLAYLISTS": { + "NO_PLAYLISTS": "未添加播放列表", + "ADD_FIRST": "从其他标签页添加你的第一个播放列表", + "ADDED_VIA_URL": "添加自URL链接:", + "ADDED_VIA_FILE": "添加自文件", + "CHANNELS": "频道", + "ADDED": "已添加的", + "SHOW_DETAILS": "显示播放列表详情", + "REMOVE": "移除播放列表", + "INFO_DIALOG": { + "PLAYLIST_DETAILS": "播放列表详情", + "TITLE": "标题", + "FROM_URL": "添加自URL链接", + "IMPORT_DATE": "导入日期", + "CHANNELS": "频道数", + "CLOSE": "关闭", + "SAVE": "保存", + "USER_AGENT": "User agent", + "ORIGINAL_FILENAME": "原始文件名", + "FILE_PATH": "文件路径", + "UPDATE_FAILED": "上次播放列表更新失败", + "AUTO_UPDATE": "自动更新", + "AUTO_UPDATE_DESCRIPTION": "如果启用了自动刷新功能,则每次启动应用程序时都会自动更新播放列表。", + "CUSTOM_USER_AGENT": "一些 IPTV 提供商需要特定的用户代理才能播放他们的播放列表。" + }, + "REMOVE_DIALOG": { + "TITLE": "移除播放列表", + "MESSAGE": "您确定要完全删除此播放列表吗?" + }, + "UPDATED": "更新", + "REFRESH": "刷新播放列表" + }, + "FILE_UPLOAD": { + "DRAG_DROP": "将文件拖放到此处", + "OR": "或", + "CHOOSE_PLAYLIST": "选择一个播放列表" + }, + "URL_UPLOAD": { + "PLAYLIST_URL": "播放一个播放列表(m3u, m3u8)", + "ADD_PLAYLIST": "添加播放列表" + } + }, + "SETTINGS": { + "TITLE": "设置 / IPTVnator", + "SUBTITLE": "更改应用程序的配置 ", + "DESCRIPTION": "请调整以下列表中的选项并使用保存按钮确认", + "EPG_URL_LABEL": "电子节目单EPG Url (*.xml 或 *.xml.gz)", + "EPG_URL_PLACEHOLDER": "输入一个 URL", + "REFRESH_EPG": "刷新电子节目单EPG", + "VIDEO_PLAYER_LABEL": "视频播放器", + "VIDEO_PLAYER_PLACEHOLDER": "选择一个选项", + "VERSION": "版本", + "SAVE_CHANGES": "保存更改", + "BACK_TO_HOME": "回到主页", + "NEW_VERSION_AVAILABLE": "有新版本可用 ", + "LATEST_VERSION": "您使用的是最新版本", + "LANGUAGE": "语言", + "SETTINGS_SAVED": "配置已成功保存", + "THEME": "主题", + "SHOW_CAPTIONS": "Show subtitles" + }, + "THEMES": { + "DARK_THEME": "暗黑模式", + "LIGHT_THEME": "明亮模式" + }, + "CHANNELS": { + "SEARCH_CHANNEL": "搜索频道", + "UPLOAD_OR_SELECT_OTHER_PLAYLIST": "上传或选择其他播放列表", + "ALL_CHANNELS": "所有频道", + "GROUPS": "分组", + "UNGROUPED": "未分组", + "UNNAMED_CHANNEL": "未命名频道", + "FAVORITES": "收藏", + "NO_FAVORITES": "还没有收藏夹", + "USE_STAR_TO_FAVORITE": "使用“☆”图标将频道添加到此列表中", + "REMOVE_FAVORITE": "从收藏夹中删除" + }, + "TOP_MENU": { + "OPEN_CHANNELS_LIST": "打开频道列表", + "TOGGLE_FAVORITE_FLAG": "切换收藏夹标志", + "OPEN_EPG_LIST": "打开电子节目单EPG列表" + }, + "EPG": { + "NEXT_DAY": "明天", + "PREVIOUS_DAY": "昨天", + "LIVE_NOW": "Live now", + "LIVE_STREAM": "Live stream", + "TIMESHIFT_AVAILABLE": "时移功能可用,点击播放", + "EPG_NOT_AVAILABLE_DATE": "错误:电子节目单EPG在所选日期不可用", + "EPG_NOT_AVAILABLE_CHANNEL_TITLE": "错误:电子节目单EPG不适用于此频道", + "EPG_NOT_AVAILABLE_CHANNEL_DESCRIPTION": "请添加/更改设置项中电子节目单的URL", + "FETCH_EPG": "获取 EPG 数据...", + "PROGRAM_DIALOG": { + "PROGRAM_DETAILS": "电视节目详情", + "TITLE": "标题", + "CATEGORY": "类别", + "PARENTAL_RATING_SYSTEM": "电视内容分级系统", + "DESCRIPTION": "描述", + "LANGUAGE": "语言", + "SHOW_PROGRAM_DETAILS": "显示有关此程序的详细信息" + } + }, + "LANGUAGES": { + "CHINESE": "简体中文", + "ENGLISH": "English", + "KOREAN": "한국어", + "RUSSIAN": "Русский", + "GERMAN": "Deutsch", + "SPANISH": "Español" + }, + "YES": "是", + "NO": "否" +}