mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 17:36:15 -08:00
feat: global subtitle display setting
You can now activate/deactivate the visiblity of subtitles on a global application level.
This commit is contained in:
15 files changed
+209
-144
No files matched your search
@@ -26,6 +26,9 @@ export class HtmlVideoPlayerComponent implements OnChanges, OnDestroy {
|
|||||||
/** HLS object */
|
/** HLS object */
|
||||||
hls: Hls;
|
hls: Hls;
|
||||||
|
|
||||||
|
/** Captions/subtitles indicator */
|
||||||
|
@Input() showCaptions!: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen for component input changes
|
* Listen for component input changes
|
||||||
* @param changes component 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
|
* Handles promise based play operation
|
||||||
*/
|
*/
|
||||||
@@ -64,6 +80,9 @@ export class HtmlVideoPlayerComponent implements OnChanges, OnDestroy {
|
|||||||
playPromise
|
playPromise
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
// Automatic playback started!
|
// Automatic playback started!
|
||||||
|
if (!this.showCaptions) {
|
||||||
|
this.disableCaptions();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {});
|
.catch((error) => {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
<!-- video.js player -->
|
<!-- video.js player -->
|
||||||
<app-vjs-player
|
<app-vjs-player
|
||||||
*ngIf="player === 'videojs'"
|
*ngIf="playerSettings.player === 'videojs'"
|
||||||
[options]="{
|
[options]="{
|
||||||
sources: [
|
sources: [
|
||||||
{
|
{
|
||||||
@@ -60,12 +60,14 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}"
|
}"
|
||||||
|
[class.hide-captions]="!playerSettings.showCaptions"
|
||||||
></app-vjs-player>
|
></app-vjs-player>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<!-- default html player component -->
|
<!-- default html player component -->
|
||||||
<app-html-video-player
|
<app-html-video-player
|
||||||
*ngIf="player === 'html5'"
|
*ngIf="playerSettings.player === 'html5'"
|
||||||
[channel]="activeChannel$ | async"
|
[channel]="activeChannel$ | async"
|
||||||
|
[showCaptions]="playerSettings.showCaptions"
|
||||||
>
|
>
|
||||||
</app-html-video-player>
|
</app-html-video-player>
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import * as MOCKED_PLAYLIST from '../../../../mocks/playlist.json';
|
|||||||
import { createChannel } from '../../../state';
|
import { createChannel } from '../../../state';
|
||||||
import { HtmlVideoPlayerComponent } from '../html-video-player/html-video-player.component';
|
import { HtmlVideoPlayerComponent } from '../html-video-player/html-video-player.component';
|
||||||
import { EpgListComponent } from '../epg-list/epg-list.component';
|
import { EpgListComponent } from '../epg-list/epg-list.component';
|
||||||
|
import { VideoPlayer } from '../../../settings/settings.interface';
|
||||||
|
|
||||||
class MatSnackBarStub {
|
class MatSnackBarStub {
|
||||||
open(): void {}
|
open(): void {}
|
||||||
@@ -73,7 +74,10 @@ describe('VideoPlayerComponent', () => {
|
|||||||
|
|
||||||
it('should check default component settings', () => {
|
it('should check default component settings', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(component.player).toEqual('videojs');
|
expect(component.playerSettings).toEqual({
|
||||||
|
player: VideoPlayer.VideoJs,
|
||||||
|
showCaptions: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update store after channel was faved', () => {
|
it('should update store after channel was faved', () => {
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { MatSidenav } from '@angular/material/sidenav';
|
import { MatSidenav } from '@angular/material/sidenav';
|
||||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||||
import {
|
import { Settings, VideoPlayer } from '../../../settings/settings.interface';
|
||||||
Settings,
|
|
||||||
VideoPlayerType,
|
|
||||||
} from '../../../settings/settings.interface';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
/** Settings key in storage */
|
/** Settings key in storage */
|
||||||
@@ -46,8 +43,11 @@ export class VideoPlayerComponent implements OnInit {
|
|||||||
(store) => store.favorites
|
(store) => store.favorites
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Selected video player component */
|
/** Selected video player options */
|
||||||
player: VideoPlayerType = 'videojs';
|
playerSettings: Partial<Settings> = {
|
||||||
|
player: VideoPlayer.VideoJs,
|
||||||
|
showCaptions: false,
|
||||||
|
};
|
||||||
|
|
||||||
/** Sidebar object */
|
/** Sidebar object */
|
||||||
@ViewChild('sidenav') sideNav: MatSidenav;
|
@ViewChild('sidenav') sideNav: MatSidenav;
|
||||||
@@ -79,7 +79,10 @@ export class VideoPlayerComponent implements OnInit {
|
|||||||
applySettings(): void {
|
applySettings(): void {
|
||||||
this.storage.get(SETTINGS_STORE_KEY).subscribe((settings: Settings) => {
|
this.storage.get(SETTINGS_STORE_KEY).subscribe((settings: Settings) => {
|
||||||
if (settings && Object.keys(settings).length > 0) {
|
if (settings && Object.keys(settings).length > 0) {
|
||||||
this.player = settings.player;
|
this.playerSettings = {
|
||||||
|
player: settings.player,
|
||||||
|
showCaptions: settings.showCaptions,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,3 +5,12 @@
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: calc(100vh - 85px);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -119,6 +119,16 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
|
<mat-list-item>
|
||||||
|
<div matLine fxLayout="row">
|
||||||
|
<p fxFlex>{{ 'SETTINGS.SHOW_CAPTIONS' | translate }}</p>
|
||||||
|
<p fxFlex>
|
||||||
|
<mat-checkbox
|
||||||
|
formControlName="showCaptions"
|
||||||
|
></mat-checkbox>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</mat-list-item>
|
||||||
<mat-list-item>
|
<mat-list-item>
|
||||||
<div matLine fxLayout="row">
|
<div matLine fxLayout="row">
|
||||||
<p fxFlex>{{ 'SETTINGS.VERSION' | translate }}</p>
|
<p fxFlex>{{ 'SETTINGS.VERSION' | translate }}</p>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||||
import { FormBuilder } from '@angular/forms';
|
import { FormBuilder } from '@angular/forms';
|
||||||
/* eslint-disable @typescript-eslint/unbound-method */
|
/* eslint-disable @typescript-eslint/unbound-method */
|
||||||
@@ -72,6 +73,7 @@ describe('SettingsComponent', () => {
|
|||||||
MockModule(MatCardModule),
|
MockModule(MatCardModule),
|
||||||
MockModule(MatListModule),
|
MockModule(MatListModule),
|
||||||
MockModule(MatFormFieldModule),
|
MockModule(MatFormFieldModule),
|
||||||
|
MockModule(MatCheckboxModule),
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { StorageMap } from '@ngx-pwa/local-storage';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { Settings } from './settings.interface';
|
import { Settings, VideoPlayer } from './settings.interface';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import { ElectronService } from '../services/electron.service';
|
import { ElectronService } from '../services/electron.service';
|
||||||
@@ -34,11 +34,11 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
/** Player options */
|
/** Player options */
|
||||||
players = [
|
players = [
|
||||||
{
|
{
|
||||||
id: 'html5',
|
id: VideoPlayer.Html5Player,
|
||||||
label: 'HTML5 Video Player',
|
label: 'HTML5 Video Player',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'videojs',
|
id: VideoPlayer.VideoJs,
|
||||||
label: 'VideoJs Player',
|
label: 'VideoJs Player',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -84,9 +84,10 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
private translate: TranslateService
|
private translate: TranslateService
|
||||||
) {
|
) {
|
||||||
this.settingsForm = this.formBuilder.group({
|
this.settingsForm = this.formBuilder.group({
|
||||||
player: ['videojs'],
|
player: [VideoPlayer.VideoJs],
|
||||||
epgUrl: '',
|
epgUrl: '',
|
||||||
language: Language.ENGLISH,
|
language: Language.ENGLISH,
|
||||||
|
showCaptions: false,
|
||||||
theme: Theme.LightTheme,
|
theme: Theme.LightTheme,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,11 +124,16 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
this.storage.get('settings').subscribe((settings: Settings) => {
|
this.storage.get('settings').subscribe((settings: Settings) => {
|
||||||
if (settings) {
|
if (settings) {
|
||||||
this.settingsForm.setValue({
|
this.settingsForm.setValue({
|
||||||
player: settings.player ? settings.player : 'videojs',
|
player: settings.player
|
||||||
|
? settings.player
|
||||||
|
: VideoPlayer.VideoJs,
|
||||||
epgUrl: settings.epgUrl ? settings.epgUrl : '',
|
epgUrl: settings.epgUrl ? settings.epgUrl : '',
|
||||||
language: settings.language
|
language: settings.language
|
||||||
? settings.language
|
? settings.language
|
||||||
: Language.ENGLISH,
|
: Language.ENGLISH,
|
||||||
|
showCaptions: settings.showCaptions
|
||||||
|
? settings.showCaptions
|
||||||
|
: false,
|
||||||
theme: settings.theme
|
theme: settings.theme
|
||||||
? settings.theme
|
? settings.theme
|
||||||
: Theme.LightTheme,
|
: Theme.LightTheme,
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { Language } from './language.enum';
|
import { Language } from './language.enum';
|
||||||
import { Theme } from './theme.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
|
* Describes all available settings options of the application
|
||||||
*/
|
*/
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
player: VideoPlayerType;
|
player: VideoPlayer;
|
||||||
epgUrl: string;
|
epgUrl: string;
|
||||||
language: Language;
|
language: Language;
|
||||||
|
showCaptions: boolean;
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"PROGRAM_DIALOG": {
|
"PROGRAM_DIALOG": {
|
||||||
"PROGRAM_DETAILS": "Details zum TV-Programm",
|
"PROGRAM_DETAILS": "Details zum TV-Programm",
|
||||||
"TITLE": "Titel",
|
"TITLE": "Titel",
|
||||||
"CATEGORY": "Ketegorie",
|
"CATEGORY": "Kategorie",
|
||||||
"PARENTAL_RATING_SYSTEM": "Alterskennzeichnung (USK)",
|
"PARENTAL_RATING_SYSTEM": "Alterskennzeichnung (USK)",
|
||||||
"DESCRIPTION": "Beschreibung",
|
"DESCRIPTION": "Beschreibung",
|
||||||
"LANGUAGE": "Sprache",
|
"LANGUAGE": "Sprache",
|
||||||
@@ -105,7 +105,8 @@
|
|||||||
"VIDEO_PLAYER_LABEL": "Video player",
|
"VIDEO_PLAYER_LABEL": "Video player",
|
||||||
"VIDEO_PLAYER_PLACEHOLDER": "Option auswählen",
|
"VIDEO_PLAYER_PLACEHOLDER": "Option auswählen",
|
||||||
"SETTINGS_SAVED": "Einstellungen wurden erfolgreich gespeichert!",
|
"SETTINGS_SAVED": "Einstellungen wurden erfolgreich gespeichert!",
|
||||||
"THEME": "Farbdesign"
|
"THEME": "Farbdesign",
|
||||||
|
"SHOW_CAPTIONS": "Untertitel anzeigen"
|
||||||
},
|
},
|
||||||
"THEMES": {
|
"THEMES": {
|
||||||
"DARK_THEME": "Dunkel",
|
"DARK_THEME": "Dunkel",
|
||||||
|
|||||||
@@ -65,7 +65,8 @@
|
|||||||
"LATEST_VERSION": "You are using the latest version",
|
"LATEST_VERSION": "You are using the latest version",
|
||||||
"LANGUAGE": "Language",
|
"LANGUAGE": "Language",
|
||||||
"SETTINGS_SAVED": "Success! Configuration was saved.",
|
"SETTINGS_SAVED": "Success! Configuration was saved.",
|
||||||
"THEME": "Visual theme"
|
"THEME": "Visual theme",
|
||||||
|
"SHOW_CAPTIONS": "Show subtitles"
|
||||||
},
|
},
|
||||||
"THEMES": {
|
"THEMES": {
|
||||||
"DARK_THEME": "Dark theme",
|
"DARK_THEME": "Dark theme",
|
||||||
|
|||||||
@@ -65,7 +65,8 @@
|
|||||||
"LATEST_VERSION": "Estás usando la versión reciente",
|
"LATEST_VERSION": "Estás usando la versión reciente",
|
||||||
"LANGUAGE": "Idioma",
|
"LANGUAGE": "Idioma",
|
||||||
"SETTINGS_SAVED": "¡Completado! La configuración se guardó.",
|
"SETTINGS_SAVED": "¡Completado! La configuración se guardó.",
|
||||||
"THEME": "Tema visual"
|
"THEME": "Tema visual",
|
||||||
|
"SHOW_CAPTIONS": "Mostrar subtítulos"
|
||||||
},
|
},
|
||||||
"THEMES": {
|
"THEMES": {
|
||||||
"DARK_THEME": "Tema oscuro",
|
"DARK_THEME": "Tema oscuro",
|
||||||
|
|||||||
@@ -65,7 +65,8 @@
|
|||||||
"LATEST_VERSION": "최신 버전을 사용하고 있습니다",
|
"LATEST_VERSION": "최신 버전을 사용하고 있습니다",
|
||||||
"LANGUAGE": "언어",
|
"LANGUAGE": "언어",
|
||||||
"SETTINGS_SAVED": "설정이 저장되었습니다.",
|
"SETTINGS_SAVED": "설정이 저장되었습니다.",
|
||||||
"THEME": "테마"
|
"THEME": "테마",
|
||||||
|
"SHOW_CAPTIONS": "Show subtitles"
|
||||||
},
|
},
|
||||||
"THEMES": {
|
"THEMES": {
|
||||||
"DARK_THEME": "어두운 테마",
|
"DARK_THEME": "어두운 테마",
|
||||||
|
|||||||
@@ -65,7 +65,8 @@
|
|||||||
"LATEST_VERSION": "Вы используете последнюю версию приложения",
|
"LATEST_VERSION": "Вы используете последнюю версию приложения",
|
||||||
"LANGUAGE": "Язык",
|
"LANGUAGE": "Язык",
|
||||||
"SETTINGS_SAVED": "Новые настройки были успешно сохранены!",
|
"SETTINGS_SAVED": "Новые настройки были успешно сохранены!",
|
||||||
"THEME": "Тема оформления"
|
"THEME": "Тема оформления",
|
||||||
|
"SHOW_CAPTIONS": "Отображать субтитры"
|
||||||
},
|
},
|
||||||
"THEMES": {
|
"THEMES": {
|
||||||
"DARK_THEME": "Тёмная тема",
|
"DARK_THEME": "Тёмная тема",
|
||||||
|
|||||||
+122
-121
@@ -1,121 +1,122 @@
|
|||||||
{
|
{
|
||||||
"HOME": {
|
"HOME": {
|
||||||
"TITLE": "IPTVnator",
|
"TITLE": "IPTVnator",
|
||||||
"SUBTITLE": "选择一个播放列表或者从URL/文件中添加一个新的播放列表",
|
"SUBTITLE": "选择一个播放列表或者从URL/文件中添加一个新的播放列表",
|
||||||
"TABS": {
|
"TABS": {
|
||||||
"RECENTLY_ADDED": "最近添加的播放列表",
|
"RECENTLY_ADDED": "最近添加的播放列表",
|
||||||
"FILE_UPLOAD": "从文件添加",
|
"FILE_UPLOAD": "从文件添加",
|
||||||
"URL_UPLOAD": "从URL链接添加"
|
"URL_UPLOAD": "从URL链接添加"
|
||||||
},
|
},
|
||||||
"PLAYLISTS": {
|
"PLAYLISTS": {
|
||||||
"NO_PLAYLISTS": "未添加播放列表",
|
"NO_PLAYLISTS": "未添加播放列表",
|
||||||
"ADD_FIRST": "从其他标签页添加你的第一个播放列表",
|
"ADD_FIRST": "从其他标签页添加你的第一个播放列表",
|
||||||
"ADDED_VIA_URL": "添加自URL链接:",
|
"ADDED_VIA_URL": "添加自URL链接:",
|
||||||
"ADDED_VIA_FILE": "添加自文件",
|
"ADDED_VIA_FILE": "添加自文件",
|
||||||
"CHANNELS": "频道",
|
"CHANNELS": "频道",
|
||||||
"ADDED": "已添加的",
|
"ADDED": "已添加的",
|
||||||
"SHOW_DETAILS": "显示播放列表详情",
|
"SHOW_DETAILS": "显示播放列表详情",
|
||||||
"REMOVE": "移除播放列表",
|
"REMOVE": "移除播放列表",
|
||||||
"INFO_DIALOG": {
|
"INFO_DIALOG": {
|
||||||
"PLAYLIST_DETAILS": "播放列表详情",
|
"PLAYLIST_DETAILS": "播放列表详情",
|
||||||
"TITLE": "标题",
|
"TITLE": "标题",
|
||||||
"FROM_URL": "添加自URL链接",
|
"FROM_URL": "添加自URL链接",
|
||||||
"IMPORT_DATE": "导入日期",
|
"IMPORT_DATE": "导入日期",
|
||||||
"CHANNELS": "频道数",
|
"CHANNELS": "频道数",
|
||||||
"CLOSE": "关闭",
|
"CLOSE": "关闭",
|
||||||
"SAVE": "保存",
|
"SAVE": "保存",
|
||||||
"USER_AGENT": "User agent",
|
"USER_AGENT": "User agent",
|
||||||
"ORIGINAL_FILENAME": "原始文件名",
|
"ORIGINAL_FILENAME": "原始文件名",
|
||||||
"FILE_PATH": "文件路径",
|
"FILE_PATH": "文件路径",
|
||||||
"UPDATE_FAILED": "上次播放列表更新失败",
|
"UPDATE_FAILED": "上次播放列表更新失败",
|
||||||
"AUTO_UPDATE": "自动更新",
|
"AUTO_UPDATE": "自动更新",
|
||||||
"AUTO_UPDATE_DESCRIPTION": "如果启用了自动刷新功能,则每次启动应用程序时都会自动更新播放列表。",
|
"AUTO_UPDATE_DESCRIPTION": "如果启用了自动刷新功能,则每次启动应用程序时都会自动更新播放列表。",
|
||||||
"CUSTOM_USER_AGENT": "一些 IPTV 提供商需要特定的用户代理才能播放他们的播放列表。"
|
"CUSTOM_USER_AGENT": "一些 IPTV 提供商需要特定的用户代理才能播放他们的播放列表。"
|
||||||
},
|
},
|
||||||
"REMOVE_DIALOG": {
|
"REMOVE_DIALOG": {
|
||||||
"TITLE": "移除播放列表",
|
"TITLE": "移除播放列表",
|
||||||
"MESSAGE": "您确定要完全删除此播放列表吗?"
|
"MESSAGE": "您确定要完全删除此播放列表吗?"
|
||||||
},
|
},
|
||||||
"UPDATED": "更新",
|
"UPDATED": "更新",
|
||||||
"REFRESH": "刷新播放列表"
|
"REFRESH": "刷新播放列表"
|
||||||
},
|
},
|
||||||
"FILE_UPLOAD": {
|
"FILE_UPLOAD": {
|
||||||
"DRAG_DROP": "将文件拖放到此处",
|
"DRAG_DROP": "将文件拖放到此处",
|
||||||
"OR": "或",
|
"OR": "或",
|
||||||
"CHOOSE_PLAYLIST": "选择一个播放列表"
|
"CHOOSE_PLAYLIST": "选择一个播放列表"
|
||||||
},
|
},
|
||||||
"URL_UPLOAD": {
|
"URL_UPLOAD": {
|
||||||
"PLAYLIST_URL": "播放一个播放列表(m3u, m3u8)",
|
"PLAYLIST_URL": "播放一个播放列表(m3u, m3u8)",
|
||||||
"ADD_PLAYLIST": "添加播放列表"
|
"ADD_PLAYLIST": "添加播放列表"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SETTINGS": {
|
"SETTINGS": {
|
||||||
"TITLE": "设置 / IPTVnator",
|
"TITLE": "设置 / IPTVnator",
|
||||||
"SUBTITLE": "更改应用程序的配置 ",
|
"SUBTITLE": "更改应用程序的配置 ",
|
||||||
"DESCRIPTION": "请调整以下列表中的选项并使用保存按钮确认",
|
"DESCRIPTION": "请调整以下列表中的选项并使用保存按钮确认",
|
||||||
"EPG_URL_LABEL": "电子节目单EPG Url (*.xml 或 *.xml.gz)",
|
"EPG_URL_LABEL": "电子节目单EPG Url (*.xml 或 *.xml.gz)",
|
||||||
"EPG_URL_PLACEHOLDER": "输入一个 URL",
|
"EPG_URL_PLACEHOLDER": "输入一个 URL",
|
||||||
"REFRESH_EPG": "刷新电子节目单EPG",
|
"REFRESH_EPG": "刷新电子节目单EPG",
|
||||||
"VIDEO_PLAYER_LABEL": "视频播放器",
|
"VIDEO_PLAYER_LABEL": "视频播放器",
|
||||||
"VIDEO_PLAYER_PLACEHOLDER": "选择一个选项",
|
"VIDEO_PLAYER_PLACEHOLDER": "选择一个选项",
|
||||||
"VERSION": "版本",
|
"VERSION": "版本",
|
||||||
"SAVE_CHANGES": "保存更改",
|
"SAVE_CHANGES": "保存更改",
|
||||||
"BACK_TO_HOME": "回到主页",
|
"BACK_TO_HOME": "回到主页",
|
||||||
"NEW_VERSION_AVAILABLE": "有新版本可用 ",
|
"NEW_VERSION_AVAILABLE": "有新版本可用 ",
|
||||||
"LATEST_VERSION": "您使用的是最新版本",
|
"LATEST_VERSION": "您使用的是最新版本",
|
||||||
"LANGUAGE": "语言",
|
"LANGUAGE": "语言",
|
||||||
"SETTINGS_SAVED": "配置已成功保存",
|
"SETTINGS_SAVED": "配置已成功保存",
|
||||||
"THEME": "主题"
|
"THEME": "主题",
|
||||||
},
|
"SHOW_CAPTIONS": "Show subtitles"
|
||||||
"THEMES": {
|
},
|
||||||
"DARK_THEME": "暗黑模式",
|
"THEMES": {
|
||||||
"LIGHT_THEME": "明亮模式"
|
"DARK_THEME": "暗黑模式",
|
||||||
},
|
"LIGHT_THEME": "明亮模式"
|
||||||
"CHANNELS": {
|
},
|
||||||
"SEARCH_CHANNEL": "搜索频道",
|
"CHANNELS": {
|
||||||
"UPLOAD_OR_SELECT_OTHER_PLAYLIST": "上传或选择其他播放列表",
|
"SEARCH_CHANNEL": "搜索频道",
|
||||||
"ALL_CHANNELS": "所有频道",
|
"UPLOAD_OR_SELECT_OTHER_PLAYLIST": "上传或选择其他播放列表",
|
||||||
"GROUPS": "分组",
|
"ALL_CHANNELS": "所有频道",
|
||||||
"UNGROUPED": "未分组",
|
"GROUPS": "分组",
|
||||||
"UNNAMED_CHANNEL": "未命名频道",
|
"UNGROUPED": "未分组",
|
||||||
"FAVORITES": "收藏",
|
"UNNAMED_CHANNEL": "未命名频道",
|
||||||
"NO_FAVORITES": "还没有收藏夹",
|
"FAVORITES": "收藏",
|
||||||
"USE_STAR_TO_FAVORITE": "使用“☆”图标将频道添加到此列表中",
|
"NO_FAVORITES": "还没有收藏夹",
|
||||||
"REMOVE_FAVORITE": "从收藏夹中删除"
|
"USE_STAR_TO_FAVORITE": "使用“☆”图标将频道添加到此列表中",
|
||||||
},
|
"REMOVE_FAVORITE": "从收藏夹中删除"
|
||||||
"TOP_MENU": {
|
},
|
||||||
"OPEN_CHANNELS_LIST": "打开频道列表",
|
"TOP_MENU": {
|
||||||
"TOGGLE_FAVORITE_FLAG": "切换收藏夹标志",
|
"OPEN_CHANNELS_LIST": "打开频道列表",
|
||||||
"OPEN_EPG_LIST": "打开电子节目单EPG列表"
|
"TOGGLE_FAVORITE_FLAG": "切换收藏夹标志",
|
||||||
},
|
"OPEN_EPG_LIST": "打开电子节目单EPG列表"
|
||||||
"EPG": {
|
},
|
||||||
"NEXT_DAY": "明天",
|
"EPG": {
|
||||||
"PREVIOUS_DAY": "昨天",
|
"NEXT_DAY": "明天",
|
||||||
"LIVE_NOW": "Live now",
|
"PREVIOUS_DAY": "昨天",
|
||||||
"LIVE_STREAM": "Live stream",
|
"LIVE_NOW": "Live now",
|
||||||
"TIMESHIFT_AVAILABLE": "时移功能可用,点击播放",
|
"LIVE_STREAM": "Live stream",
|
||||||
"EPG_NOT_AVAILABLE_DATE": "错误:电子节目单EPG在所选日期不可用",
|
"TIMESHIFT_AVAILABLE": "时移功能可用,点击播放",
|
||||||
"EPG_NOT_AVAILABLE_CHANNEL_TITLE": "错误:电子节目单EPG不适用于此频道",
|
"EPG_NOT_AVAILABLE_DATE": "错误:电子节目单EPG在所选日期不可用",
|
||||||
"EPG_NOT_AVAILABLE_CHANNEL_DESCRIPTION": "请添加/更改设置项中电子节目单的URL",
|
"EPG_NOT_AVAILABLE_CHANNEL_TITLE": "错误:电子节目单EPG不适用于此频道",
|
||||||
"FETCH_EPG": "获取 EPG 数据...",
|
"EPG_NOT_AVAILABLE_CHANNEL_DESCRIPTION": "请添加/更改设置项中电子节目单的URL",
|
||||||
"PROGRAM_DIALOG": {
|
"FETCH_EPG": "获取 EPG 数据...",
|
||||||
"PROGRAM_DETAILS": "电视节目详情",
|
"PROGRAM_DIALOG": {
|
||||||
"TITLE": "标题",
|
"PROGRAM_DETAILS": "电视节目详情",
|
||||||
"CATEGORY": "类别",
|
"TITLE": "标题",
|
||||||
"PARENTAL_RATING_SYSTEM": "电视内容分级系统",
|
"CATEGORY": "类别",
|
||||||
"DESCRIPTION": "描述",
|
"PARENTAL_RATING_SYSTEM": "电视内容分级系统",
|
||||||
"LANGUAGE": "语言",
|
"DESCRIPTION": "描述",
|
||||||
"SHOW_PROGRAM_DETAILS": "显示有关此程序的详细信息"
|
"LANGUAGE": "语言",
|
||||||
}
|
"SHOW_PROGRAM_DETAILS": "显示有关此程序的详细信息"
|
||||||
},
|
}
|
||||||
"LANGUAGES": {
|
},
|
||||||
"CHINESE": "简体中文",
|
"LANGUAGES": {
|
||||||
"ENGLISH": "English",
|
"CHINESE": "简体中文",
|
||||||
"KOREAN": "한국어",
|
"ENGLISH": "English",
|
||||||
"RUSSIAN": "Русский",
|
"KOREAN": "한국어",
|
||||||
"GERMAN": "Deutsch",
|
"RUSSIAN": "Русский",
|
||||||
"SPANISH": "Español"
|
"GERMAN": "Deutsch",
|
||||||
},
|
"SPANISH": "Español"
|
||||||
"YES": "是",
|
},
|
||||||
"NO": "否"
|
"YES": "是",
|
||||||
}
|
"NO": "否"
|
||||||
|
}
|
||||||
Reference in new issue
Block a user