mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 21:56:18 -08:00
Merge origin/master into claude/parental-control-feature-31dde2
Resolves the settings-store defaults split, the electron-conf key list and the guidance reorganization (CLAUDE.md now imports AGENTS.md; the parental lock contract is linked from the agent context map instead). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
ae5d3f49de
732 files changed
+42572
-15380
No files matched your search
@@ -1,32 +1,39 @@
|
||||
// Cover size tokens — consumed by all cover/poster grids and rails.
|
||||
// Toggled via [data-cover-size] attribute on <html>, set from SettingsStore.
|
||||
// --season-cover-width sizes the season cover beside the season tabs on the
|
||||
// series detail page; medium matches the About block's 120px poster.
|
||||
:root {
|
||||
--cover-grid-min-width: 148px;
|
||||
--cover-rail-width: 172px;
|
||||
--cover-gap: 16px;
|
||||
--season-cover-width: 120px;
|
||||
}
|
||||
|
||||
:root[data-cover-size='small'] {
|
||||
--cover-grid-min-width: 120px;
|
||||
--cover-rail-width: 140px;
|
||||
--cover-gap: 12px;
|
||||
--season-cover-width: 96px;
|
||||
}
|
||||
|
||||
:root[data-cover-size='medium'] {
|
||||
--cover-grid-min-width: 148px;
|
||||
--cover-rail-width: 172px;
|
||||
--cover-gap: 16px;
|
||||
--season-cover-width: 120px;
|
||||
}
|
||||
|
||||
:root[data-cover-size='large'] {
|
||||
--cover-grid-min-width: 200px;
|
||||
--cover-rail-width: 220px;
|
||||
--cover-gap: 20px;
|
||||
--season-cover-width: 144px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
:root[data-cover-size='large'] {
|
||||
--cover-grid-min-width: 160px;
|
||||
--cover-rail-width: 170px;
|
||||
--season-cover-width: 120px;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ const availableStatus: ElectronBridgeAppUpdateStatus = {
|
||||
manualDownloadUrl: 'https://github.com/4gray/iptvnator/releases/latest',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Available,
|
||||
supportedSelfUpdate: true,
|
||||
channel: 'stable',
|
||||
installedChannel: 'stable',
|
||||
};
|
||||
|
||||
describe('AppUpdateNotificationPanelComponent', () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
OnInit,
|
||||
signal,
|
||||
inject,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
@@ -101,6 +102,8 @@ import { AppUpdateReleaseNotesDialogComponent } from './settings/app-update-rele
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [
|
||||
`
|
||||
.app-update-notification {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
inject,
|
||||
OnInit,
|
||||
signal,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router, RouterOutlet } from '@angular/router';
|
||||
@@ -50,6 +51,8 @@ const debugAppComponent = createDevLogger('AppComponent');
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
imports: [
|
||||
AppStartupStatusComponent,
|
||||
AppUpdateNotificationPanelComponent,
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
HttpClient,
|
||||
provideHttpClient,
|
||||
withInterceptorsFromDi,
|
||||
withXhr,
|
||||
} from '@angular/common/http';
|
||||
import {
|
||||
FullscreenOverlayContainer,
|
||||
@@ -115,7 +116,7 @@ export const appConfig: ApplicationConfig = {
|
||||
// fullscreen channel panel's sort/context menus and the controls'
|
||||
// tooltips render while the player is fullscreen.
|
||||
{ provide: OverlayContainer, useClass: FullscreenOverlayContainer },
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClient(withXhr(), withInterceptorsFromDi()),
|
||||
provideStore({
|
||||
router: routerReducer,
|
||||
playlistState: playlistReducer,
|
||||
|
||||
@@ -17,9 +17,22 @@ class MockXtreamCollectionDetailComponent {
|
||||
readonly closeRequested = output<void>();
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-stalker-collection-detail',
|
||||
template: '',
|
||||
})
|
||||
class MockStalkerCollectionDetailComponent {
|
||||
readonly item = input<UnifiedCollectionItem | null>(null);
|
||||
readonly seriesResume = input<SeriesResumeTarget | null>(null);
|
||||
readonly closeRequested = output<void>();
|
||||
}
|
||||
|
||||
jest.unstable_mockModule('@iptvnator/portal/xtream/feature', () => ({
|
||||
XtreamCollectionDetailComponent: MockXtreamCollectionDetailComponent,
|
||||
}));
|
||||
jest.unstable_mockModule('@iptvnator/portal/stalker/feature', () => ({
|
||||
StalkerCollectionDetailComponent: MockStalkerCollectionDetailComponent,
|
||||
}));
|
||||
|
||||
@Component({
|
||||
imports: [GlobalCollectionDetailHostComponent],
|
||||
@@ -123,6 +136,38 @@ describe('GlobalCollectionDetailHostComponent', () => {
|
||||
expect(instance.seriesResume()).toEqual(seriesResume);
|
||||
});
|
||||
|
||||
it('creates the Stalker detail with the series resume target attached', async () => {
|
||||
// A Stalker embedded-VOD show routes as a movie; the resume target
|
||||
// still has to reach the detail so the series view can consume it.
|
||||
const seriesResume = {
|
||||
seriesXtreamId: 17672,
|
||||
contentXtreamId: 1879880442,
|
||||
seasonNumber: 2,
|
||||
episodeNumber: 6,
|
||||
};
|
||||
fixture.componentInstance.seriesResume.set(seriesResume);
|
||||
fixture.componentInstance.item.set({
|
||||
uid: 'stalker::stalker-1::17672',
|
||||
name: 'Old School',
|
||||
contentType: 'movie',
|
||||
sourceType: 'stalker',
|
||||
playlistId: 'stalker-1',
|
||||
playlistName: 'Portal',
|
||||
stalkerId: '17672',
|
||||
categoryId: '7',
|
||||
});
|
||||
await stabilize();
|
||||
|
||||
const detail = fixture.debugElement.query(
|
||||
By.directive(MockStalkerCollectionDetailComponent)
|
||||
);
|
||||
expect(detail).not.toBeNull();
|
||||
const instance =
|
||||
detail?.componentInstance as MockStalkerCollectionDetailComponent;
|
||||
expect(instance.item()?.stalkerId).toBe('17672');
|
||||
expect(instance.seriesResume()).toEqual(seriesResume);
|
||||
});
|
||||
|
||||
it('re-emits close requests and clears the detail with the item', async () => {
|
||||
fixture.componentInstance.item.set(xtreamSeriesItem);
|
||||
await stabilize();
|
||||
|
||||
@@ -113,9 +113,9 @@ export class GlobalCollectionDetailHostComponent implements OnDestroy {
|
||||
environmentInjector: this.environmentInjector,
|
||||
});
|
||||
componentRef.setInput('item', item);
|
||||
if (item.sourceType === 'xtream') {
|
||||
componentRef.setInput('seriesResume', seriesResume);
|
||||
}
|
||||
// Both portal details accept the one-shot resume handoff (Xtream
|
||||
// series, Stalker embedded-VOD / lazy is_series shows).
|
||||
componentRef.setInput('seriesResume', seriesResume);
|
||||
this.subscribeToClose(componentRef.instance);
|
||||
}
|
||||
|
||||
@@ -196,6 +196,7 @@ export class GlobalCollectionDetailHostComponent implements OnDestroy {
|
||||
} @else if (item.sourceType === 'stalker') {
|
||||
<app-global-collection-detail-host
|
||||
[item]="item"
|
||||
[seriesResume]="seriesResume"
|
||||
(closeRequested)="close()"
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ const BASE_STATUS: ElectronBridgeAppUpdateStatus = {
|
||||
currentVersion: '0.23.0',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Idle,
|
||||
supportedSelfUpdate: true,
|
||||
manualDownloadUrl: 'https://github.com/4gray/iptvnator/releases/latest',
|
||||
channel: 'stable',
|
||||
installedChannel: 'stable',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,8 +60,9 @@ describe('AppUpdateReleaseNotesDialogComponent', () => {
|
||||
version: '0.23.0',
|
||||
});
|
||||
expect(
|
||||
fixture.nativeElement.querySelector('[data-test-id="release-notes-body"] h2')
|
||||
?.textContent
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-body"] h2'
|
||||
)?.textContent
|
||||
).toContain('v0.23.0');
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
@@ -91,6 +92,128 @@ describe('AppUpdateReleaseNotesDialogComponent', () => {
|
||||
expect(image.classList).toContain('release-notes-dialog__image');
|
||||
});
|
||||
|
||||
it('explains a version without a GitHub release and links to the channel release list', async () => {
|
||||
(
|
||||
window.electron.getAppUpdateReleaseNotes as jest.Mock
|
||||
).mockRejectedValueOnce(
|
||||
new Error(
|
||||
"Error invoking remote method 'APP_UPDATE:GET_RELEASE_NOTES': Error: Release notes were not found for 0.23.0"
|
||||
)
|
||||
);
|
||||
const openSpy = jest
|
||||
.spyOn(window, 'open')
|
||||
.mockImplementation(() => null);
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
const error = fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-error"]'
|
||||
) as HTMLElement;
|
||||
expect(error.textContent).toContain(
|
||||
'SETTINGS.APP_UPDATE_RELEASE_NOTES_NOT_FOUND'
|
||||
);
|
||||
expect(error.textContent).not.toContain('Error invoking remote method');
|
||||
expect(error.classList).not.toContain(
|
||||
'release-notes-dialog__error--failed'
|
||||
);
|
||||
expect(fixture.componentInstance.error()).toMatchObject({
|
||||
kind: 'not-found',
|
||||
version: '0.23.0',
|
||||
});
|
||||
|
||||
(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-open-releases"]'
|
||||
) as HTMLButtonElement
|
||||
).click();
|
||||
|
||||
expect(openSpy).toHaveBeenCalledWith(
|
||||
'https://github.com/4gray/iptvnator/releases',
|
||||
'_blank',
|
||||
'noreferrer'
|
||||
);
|
||||
openSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('shows a generic failure with the underlying reason for other errors', async () => {
|
||||
(
|
||||
window.electron.getAppUpdateReleaseNotes as jest.Mock
|
||||
).mockRejectedValueOnce(
|
||||
new Error(
|
||||
"Error invoking remote method 'APP_UPDATE:GET_RELEASE_NOTES': Error: GitHub releases request failed: 403 rate limit exceeded"
|
||||
)
|
||||
);
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
const error = fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-error"]'
|
||||
) as HTMLElement;
|
||||
expect(error.classList).toContain(
|
||||
'release-notes-dialog__error--failed'
|
||||
);
|
||||
expect(error.textContent).toContain(
|
||||
'SETTINGS.APP_UPDATE_RELEASE_NOTES_LOAD_FAILED'
|
||||
);
|
||||
expect(error.textContent).toContain(
|
||||
'GitHub releases request failed: 403 rate limit exceeded'
|
||||
);
|
||||
expect(error.textContent).not.toContain('Error invoking remote method');
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-open-releases"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('offers the release list, not the earlier release, when paging fails', async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-open-release"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
|
||||
(
|
||||
window.electron.getAppUpdateReleaseNotes as jest.Mock
|
||||
).mockRejectedValueOnce(
|
||||
new Error('No release notes are available in that direction')
|
||||
);
|
||||
(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-previous"]'
|
||||
) as HTMLButtonElement
|
||||
).click();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
// The earlier notes stay for navigation, but the body is the error,
|
||||
// so the action must match the body.
|
||||
expect(fixture.componentInstance.notes()?.tagName).toBe('v0.23.0');
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-error"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-open-release"]'
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
fixture.nativeElement.querySelector(
|
||||
'[data-test-id="release-notes-open-releases"]'
|
||||
)
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('loads previous notes lazily without closing the dialog', async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -114,7 +237,9 @@ describe('AppUpdateReleaseNotesDialogComponent', () => {
|
||||
).click();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(window.electron.getAppUpdateReleaseNotes).toHaveBeenLastCalledWith({
|
||||
expect(
|
||||
window.electron.getAppUpdateReleaseNotes
|
||||
).toHaveBeenLastCalledWith({
|
||||
direction: 'previous',
|
||||
version: 'v0.23.0',
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
SecurityContext,
|
||||
signal,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
@@ -20,13 +21,27 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { marked } from 'marked';
|
||||
import {
|
||||
appUpdateReleasesListUrl,
|
||||
appVersionChannel,
|
||||
ElectronBridgeAppUpdateReleaseNotes,
|
||||
ElectronBridgeAppUpdateReleaseNotesDirection,
|
||||
ElectronBridgeAppUpdateReleaseNotesRequest,
|
||||
isAppUpdateReleaseNotesNotFoundMessage,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
|
||||
const RELEASE_NOTES_IMAGE_CLASS = 'release-notes-dialog__image';
|
||||
|
||||
/** `ipcRenderer.invoke` wraps a main-process rejection; the wrapper is noise. */
|
||||
const IPC_REJECTION_PREFIX =
|
||||
/^Error invoking remote method '[^']*': (?:Error: )?/;
|
||||
|
||||
interface ReleaseNotesLoadError {
|
||||
/** `not-found`: GitHub has no release for that version; `failed`: anything else. */
|
||||
kind: 'not-found' | 'failed';
|
||||
message: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface AppUpdateReleaseNotesDialogData {
|
||||
initialVersion?: string;
|
||||
fallbackToLatest?: boolean;
|
||||
@@ -76,7 +91,9 @@ function decorateReleaseNotesHtml(html: string): string {
|
||||
</button>
|
||||
|
||||
<div class="release-notes-dialog__version">
|
||||
<strong>{{ notes()?.releaseName || notes()?.tagName }}</strong>
|
||||
<strong>{{
|
||||
notes()?.releaseName || notes()?.tagName
|
||||
}}</strong>
|
||||
@if (notes()?.publishedAt; as publishedAt) {
|
||||
<span>{{ publishedAt | date: 'mediumDate' }}</span>
|
||||
}
|
||||
@@ -97,8 +114,31 @@ function decorateReleaseNotesHtml(html: string): string {
|
||||
<div class="release-notes-dialog__loading">
|
||||
<mat-spinner diameter="28" />
|
||||
</div>
|
||||
} @else if (error()) {
|
||||
<p class="release-notes-dialog__error">{{ error() }}</p>
|
||||
} @else if (error(); as loadError) {
|
||||
<div
|
||||
class="release-notes-dialog__error"
|
||||
[class.release-notes-dialog__error--failed]="
|
||||
loadError.kind === 'failed'
|
||||
"
|
||||
data-test-id="release-notes-error"
|
||||
>
|
||||
@if (loadError.kind === 'not-found') {
|
||||
<p>
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_RELEASE_NOTES_NOT_FOUND'
|
||||
| translate: { version: loadError.version }
|
||||
}}
|
||||
</p>
|
||||
} @else {
|
||||
<p>
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_RELEASE_NOTES_LOAD_FAILED'
|
||||
| translate
|
||||
}}
|
||||
</p>
|
||||
<small>{{ loadError.message }}</small>
|
||||
}
|
||||
</div>
|
||||
} @else {
|
||||
<article
|
||||
class="release-notes-dialog__body"
|
||||
@@ -109,8 +149,26 @@ function decorateReleaseNotesHtml(html: string): string {
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
@if (notes()?.htmlUrl; as htmlUrl) {
|
||||
<button mat-button type="button" (click)="openRelease(htmlUrl)">
|
||||
<!-- The error is checked first: a failed Previous/Next keeps the
|
||||
earlier notes for navigation, but the body shows the error,
|
||||
so the action must not open that earlier release. -->
|
||||
@if (error()) {
|
||||
<button
|
||||
mat-button
|
||||
type="button"
|
||||
(click)="openRelease(releasesPageUrl())"
|
||||
data-test-id="release-notes-open-releases"
|
||||
>
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
{{ 'SETTINGS.APP_UPDATE_OPEN_RELEASES_PAGE' | translate }}
|
||||
</button>
|
||||
} @else if (notes()?.htmlUrl; as htmlUrl) {
|
||||
<button
|
||||
mat-button
|
||||
type="button"
|
||||
(click)="openRelease(htmlUrl)"
|
||||
data-test-id="release-notes-open-release"
|
||||
>
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
{{ 'SETTINGS.APP_UPDATE_OPEN_RELEASE' | translate }}
|
||||
</button>
|
||||
@@ -120,6 +178,8 @@ function decorateReleaseNotesHtml(html: string): string {
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
`,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [
|
||||
`
|
||||
.release-notes-dialog {
|
||||
@@ -162,9 +222,25 @@ function decorateReleaseNotesHtml(html: string): string {
|
||||
}
|
||||
|
||||
.release-notes-dialog__error {
|
||||
padding: 16px 2px 0;
|
||||
color: var(--app-body-color);
|
||||
}
|
||||
|
||||
.release-notes-dialog__error--failed {
|
||||
color: var(--app-error-color, #ef4444);
|
||||
}
|
||||
|
||||
.release-notes-dialog__error p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.release-notes-dialog__error small {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
color: var(--app-muted-color);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.release-notes-dialog__body {
|
||||
max-height: min(58vh, 560px);
|
||||
overflow: auto;
|
||||
@@ -210,17 +286,26 @@ function decorateReleaseNotesHtml(html: string): string {
|
||||
],
|
||||
})
|
||||
export class AppUpdateReleaseNotesDialogComponent implements OnInit {
|
||||
private readonly data = inject<AppUpdateReleaseNotesDialogData>(
|
||||
MAT_DIALOG_DATA
|
||||
);
|
||||
private readonly data =
|
||||
inject<AppUpdateReleaseNotesDialogData>(MAT_DIALOG_DATA);
|
||||
private readonly dialogRef = inject(
|
||||
MatDialogRef<AppUpdateReleaseNotesDialogComponent>
|
||||
);
|
||||
private readonly sanitizer = inject(DomSanitizer);
|
||||
|
||||
readonly loading = signal(false);
|
||||
readonly error = signal<string | null>(null);
|
||||
readonly error = signal<ReleaseNotesLoadError | null>(null);
|
||||
readonly notes = signal<ElectronBridgeAppUpdateReleaseNotes | null>(null);
|
||||
/** Release list of the channel the failed request was about. */
|
||||
readonly releasesPageUrl = computed(() =>
|
||||
appUpdateReleasesListUrl(
|
||||
appVersionChannel(
|
||||
this.error()?.version ??
|
||||
this.notes()?.version ??
|
||||
this.data.initialVersion
|
||||
)
|
||||
)
|
||||
);
|
||||
readonly renderedMarkdown = computed(() => {
|
||||
const markdown = this.notes()?.bodyMarkdown ?? '';
|
||||
const html = marked.parse(markdown, { async: false }) as string;
|
||||
@@ -238,7 +323,10 @@ export class AppUpdateReleaseNotesDialogComponent implements OnInit {
|
||||
direction?: ElectronBridgeAppUpdateReleaseNotesDirection
|
||||
): Promise<void> {
|
||||
if (!window.electron?.getAppUpdateReleaseNotes) {
|
||||
this.error.set('Release notes are not available in this build.');
|
||||
this.error.set({
|
||||
kind: 'failed',
|
||||
message: 'Release notes are not available in this build.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,7 +359,17 @@ export class AppUpdateReleaseNotesDialogComponent implements OnInit {
|
||||
await window.electron.getAppUpdateReleaseNotes(request)
|
||||
);
|
||||
} catch (error) {
|
||||
this.error.set(error instanceof Error ? error.message : String(error));
|
||||
const message = (
|
||||
error instanceof Error ? error.message : String(error)
|
||||
).replace(IPC_REJECTION_PREFIX, '');
|
||||
|
||||
this.error.set({
|
||||
kind: isAppUpdateReleaseNotesNotFoundMessage(message)
|
||||
? 'not-found'
|
||||
: 'failed',
|
||||
message,
|
||||
version,
|
||||
});
|
||||
} finally {
|
||||
this.loading.set(false);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
<section
|
||||
class="settings-group"
|
||||
id="about"
|
||||
>
|
||||
<section class="settings-group" id="about">
|
||||
<header class="settings-group__header">
|
||||
<div class="settings-group__header-icon">
|
||||
<mat-icon>info</mat-icon>
|
||||
@@ -40,7 +37,19 @@
|
||||
<p>{{ 'SETTINGS.APP_UPDATE_DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
<div class="setting-item__control app-update-panel__control">
|
||||
<div class="app-update-status" data-test-id="app-update-status">
|
||||
<div
|
||||
class="app-update-status"
|
||||
[class.app-update-status--stale]="hasPendingChannelChange()"
|
||||
data-test-id="app-update-status"
|
||||
>
|
||||
@if (appliedChannelLabelKey(); as channelLabelKey) {
|
||||
<span
|
||||
class="app-update-status__channel"
|
||||
data-test-id="app-update-status-channel"
|
||||
>
|
||||
{{ channelLabelKey | translate }}
|
||||
</span>
|
||||
}
|
||||
<strong>
|
||||
{{
|
||||
appUpdateStatusLabelKey()
|
||||
@@ -62,18 +71,136 @@
|
||||
data-test-id="app-update-progress"
|
||||
></progress>
|
||||
}
|
||||
@if (hasPendingChannelChange()) {
|
||||
<span data-test-id="app-update-channel-pending-hint">
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_PENDING_HINT'
|
||||
| translate
|
||||
: {
|
||||
current:
|
||||
(channelLabelKey(
|
||||
verdictChannel()
|
||||
) | translate),
|
||||
next:
|
||||
(channelLabelKey(
|
||||
draftUpdateChannel()
|
||||
) | translate),
|
||||
}
|
||||
}}
|
||||
</span>
|
||||
} @else if (hasRetainedOtherChannelVerdict()) {
|
||||
<span data-test-id="app-update-channel-retained-hint">
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_RETAINED_HINT'
|
||||
| translate
|
||||
: {
|
||||
current:
|
||||
(channelLabelKey(
|
||||
verdictChannel()
|
||||
) | translate),
|
||||
next:
|
||||
(channelLabelKey(
|
||||
updateStatus.channel
|
||||
) | translate),
|
||||
}
|
||||
}}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div class="app-update-actions">
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
(click)="checkForAppUpdate.emit()"
|
||||
[disabled]="isAppUpdateBusy()"
|
||||
data-test-id="app-update-check"
|
||||
@if (canSelectUpdateChannel()) {
|
||||
<div
|
||||
class="app-update-channel"
|
||||
[formGroup]="form()!"
|
||||
data-test-id="app-update-channel"
|
||||
>
|
||||
<mat-icon>sync</mat-icon>
|
||||
{{ 'SETTINGS.APP_UPDATE_CHECK' | translate }}
|
||||
</button>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{
|
||||
'SETTINGS.APP_UPDATE_CHANNEL' | translate
|
||||
}}</mat-label>
|
||||
<mat-select
|
||||
formControlName="updateChannel"
|
||||
data-test-id="select-update-channel"
|
||||
>
|
||||
@for (
|
||||
option of updateChannelOptions();
|
||||
track option.value
|
||||
) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[attr.data-test-id]="
|
||||
'update-channel-' + option.value
|
||||
"
|
||||
>
|
||||
{{ option.labelKey | translate }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<small class="app-update-channel__note">
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_DESCRIPTION'
|
||||
| translate
|
||||
}}
|
||||
</small>
|
||||
@if (
|
||||
form()!.get('updateChannel')?.value === 'nightly'
|
||||
) {
|
||||
<small
|
||||
class="app-update-channel__note"
|
||||
data-test-id="app-update-channel-nightly-warning"
|
||||
>
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY_WARNING'
|
||||
| translate
|
||||
}}
|
||||
</small>
|
||||
}
|
||||
@if (isNightlyBuildOnStableChannel()) {
|
||||
<small
|
||||
class="app-update-channel__note"
|
||||
data-test-id="app-update-nightly-on-stable-hint"
|
||||
>
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_NIGHTLY_ON_STABLE_HINT'
|
||||
| translate
|
||||
}}
|
||||
</small>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="app-update-actions">
|
||||
@if (canSaveAndCheckForAppUpdate()) {
|
||||
<button
|
||||
mat-flat-button
|
||||
type="button"
|
||||
(click)="saveAndCheckForAppUpdate.emit()"
|
||||
[disabled]="isAppUpdateBusy() || form()!.invalid"
|
||||
data-test-id="app-update-save-and-check"
|
||||
>
|
||||
<mat-icon>save</mat-icon>
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_SAVE_AND_CHECK'
|
||||
| translate
|
||||
: {
|
||||
channel:
|
||||
(channelLabelKey(
|
||||
draftUpdateChannel()
|
||||
) | translate),
|
||||
}
|
||||
}}
|
||||
</button>
|
||||
} @else {
|
||||
<button
|
||||
mat-stroked-button
|
||||
type="button"
|
||||
(click)="checkForAppUpdate.emit()"
|
||||
[disabled]="isAppUpdateBusy()"
|
||||
data-test-id="app-update-check"
|
||||
>
|
||||
<mat-icon>sync</mat-icon>
|
||||
{{ 'SETTINGS.APP_UPDATE_CHECK' | translate }}
|
||||
</button>
|
||||
}
|
||||
@if (canDownloadAppUpdate()) {
|
||||
<button
|
||||
mat-flat-button
|
||||
@@ -94,8 +221,7 @@
|
||||
>
|
||||
<mat-icon>article</mat-icon>
|
||||
{{
|
||||
'SETTINGS.APP_UPDATE_RELEASE_NOTES'
|
||||
| translate
|
||||
'SETTINGS.APP_UPDATE_RELEASE_NOTES' | translate
|
||||
}}
|
||||
</button>
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import {
|
||||
ELECTRON_BRIDGE_APP_UPDATE_STATUSES,
|
||||
ElectronBridgeAppUpdateStatus,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { SettingsAboutSectionComponent } from './settings-about-section.component';
|
||||
import { SETTINGS_UPDATE_CHANNEL_OPTIONS } from './settings-options';
|
||||
|
||||
function getButton(fixture: ComponentFixture<SettingsAboutSectionComponent>, id: string) {
|
||||
function getButton(
|
||||
fixture: ComponentFixture<SettingsAboutSectionComponent>,
|
||||
id: string
|
||||
) {
|
||||
return fixture.nativeElement.querySelector(
|
||||
`[data-test-id="${id}"]`
|
||||
) as HTMLButtonElement | null;
|
||||
@@ -28,10 +33,7 @@ describe('SettingsAboutSectionComponent app updates', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
SettingsAboutSectionComponent,
|
||||
TranslateModule.forRoot(),
|
||||
],
|
||||
imports: [SettingsAboutSectionComponent, TranslateModule.forRoot()],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SettingsAboutSectionComponent);
|
||||
@@ -41,7 +43,9 @@ describe('SettingsAboutSectionComponent app updates', () => {
|
||||
const download = jest.fn();
|
||||
fixture.componentInstance.downloadAppUpdate.subscribe(download);
|
||||
const openNotes = jest.fn();
|
||||
fixture.componentInstance.openAppUpdateReleaseNotes.subscribe(openNotes);
|
||||
fixture.componentInstance.openAppUpdateReleaseNotes.subscribe(
|
||||
openNotes
|
||||
);
|
||||
configureComponent(fixture, {
|
||||
currentVersion: '0.22.0',
|
||||
latestVersion: '0.23.0',
|
||||
@@ -82,7 +86,9 @@ describe('SettingsAboutSectionComponent app updates', () => {
|
||||
|
||||
it('shows release notes for the current version when no update is available', () => {
|
||||
const openNotes = jest.fn();
|
||||
fixture.componentInstance.openAppUpdateReleaseNotes.subscribe(openNotes);
|
||||
fixture.componentInstance.openAppUpdateReleaseNotes.subscribe(
|
||||
openNotes
|
||||
);
|
||||
configureComponent(fixture, {
|
||||
currentVersion: '0.22.0',
|
||||
latestVersion: '0.22.0',
|
||||
@@ -137,10 +143,7 @@ describe('SettingsAboutSectionComponent version display', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
SettingsAboutSectionComponent,
|
||||
TranslateModule.forRoot(),
|
||||
],
|
||||
imports: [SettingsAboutSectionComponent, TranslateModule.forRoot()],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SettingsAboutSectionComponent);
|
||||
@@ -187,3 +190,233 @@ describe('SettingsAboutSectionComponent version display', () => {
|
||||
expect(getCommitMarker()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SettingsAboutSectionComponent update channel', () => {
|
||||
let fixture: ComponentFixture<SettingsAboutSectionComponent>;
|
||||
|
||||
const query = (id: string) =>
|
||||
fixture.nativeElement.querySelector(
|
||||
`[data-test-id="${id}"]`
|
||||
) as HTMLElement | null;
|
||||
|
||||
const configure = (
|
||||
status: Partial<ElectronBridgeAppUpdateStatus>,
|
||||
form: FormGroup | null
|
||||
) => {
|
||||
fixture.componentRef.setInput('isDesktop', true);
|
||||
fixture.componentRef.setInput('form', form);
|
||||
fixture.componentRef.setInput(
|
||||
'updateChannelOptions',
|
||||
SETTINGS_UPDATE_CHANNEL_OPTIONS
|
||||
);
|
||||
fixture.componentRef.setInput('appUpdateStatus', {
|
||||
currentVersion: '0.23.0',
|
||||
manualDownloadUrl:
|
||||
'https://github.com/4gray/iptvnator/releases/latest',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Idle,
|
||||
supportedSelfUpdate: true,
|
||||
channel: 'stable',
|
||||
installedChannel: 'stable',
|
||||
...status,
|
||||
});
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SettingsAboutSectionComponent, TranslateModule.forRoot()],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SettingsAboutSectionComponent);
|
||||
});
|
||||
|
||||
it('binds the channel select to the shared settings form', () => {
|
||||
const form = new FormGroup({
|
||||
updateChannel: new FormControl('stable'),
|
||||
});
|
||||
configure({}, form);
|
||||
|
||||
expect(query('select-update-channel')).not.toBeNull();
|
||||
expect(query('app-update-channel-nightly-warning')).toBeNull();
|
||||
|
||||
form.controls.updateChannel.setValue('nightly');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(query('app-update-channel-nightly-warning')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders no channel control without a form to bind to', () => {
|
||||
configure({}, null);
|
||||
|
||||
expect(query('select-update-channel')).toBeNull();
|
||||
});
|
||||
|
||||
it('explains that a nightly build on the stable channel waits for the next release', () => {
|
||||
const form = new FormGroup({
|
||||
updateChannel: new FormControl('stable'),
|
||||
});
|
||||
configure(
|
||||
{
|
||||
currentVersion: '0.23.1-nightly.20260915.7',
|
||||
installedChannel: 'nightly',
|
||||
},
|
||||
form
|
||||
);
|
||||
|
||||
expect(query('app-update-nightly-on-stable-hint')).not.toBeNull();
|
||||
|
||||
configure({ channel: 'nightly', installedChannel: 'nightly' }, form);
|
||||
|
||||
expect(query('app-update-nightly-on-stable-hint')).toBeNull();
|
||||
});
|
||||
|
||||
it('labels the verdict with the channel it describes', () => {
|
||||
configure({ channel: 'stable' }, null);
|
||||
|
||||
expect(query('app-update-status-channel')?.textContent?.trim()).toBe(
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_STABLE'
|
||||
);
|
||||
|
||||
configure({ channel: 'nightly' }, null);
|
||||
|
||||
expect(query('app-update-status-channel')?.textContent?.trim()).toBe(
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY'
|
||||
);
|
||||
});
|
||||
|
||||
it('offers Save and check instead of a plain check while the select shows another channel', () => {
|
||||
const check = jest.fn();
|
||||
const saveAndCheck = jest.fn();
|
||||
fixture.componentInstance.checkForAppUpdate.subscribe(check);
|
||||
fixture.componentInstance.saveAndCheckForAppUpdate.subscribe(
|
||||
saveAndCheck
|
||||
);
|
||||
const form = new FormGroup({
|
||||
updateChannel: new FormControl('stable'),
|
||||
});
|
||||
configure(
|
||||
{
|
||||
channel: 'stable',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.NotAvailable,
|
||||
},
|
||||
form
|
||||
);
|
||||
|
||||
expect(query('app-update-check')).not.toBeNull();
|
||||
expect(query('app-update-save-and-check')).toBeNull();
|
||||
expect(query('app-update-channel-pending-hint')).toBeNull();
|
||||
|
||||
form.controls.updateChannel.setValue('nightly');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(query('app-update-check')).toBeNull();
|
||||
expect(query('app-update-channel-pending-hint')).not.toBeNull();
|
||||
expect(
|
||||
query('app-update-status')?.classList.contains(
|
||||
'app-update-status--stale'
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
query('app-update-save-and-check')?.click();
|
||||
|
||||
expect(saveAndCheck).toHaveBeenCalledTimes(1);
|
||||
expect(check).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns to the plain check once the saved channel matches the select', () => {
|
||||
const form = new FormGroup({
|
||||
updateChannel: new FormControl('nightly'),
|
||||
});
|
||||
configure({ channel: 'stable' }, form);
|
||||
|
||||
expect(query('app-update-save-and-check')).not.toBeNull();
|
||||
|
||||
configure(
|
||||
{
|
||||
channel: 'nightly',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Checking,
|
||||
},
|
||||
form
|
||||
);
|
||||
|
||||
expect(query('app-update-save-and-check')).toBeNull();
|
||||
expect(query('app-update-channel-pending-hint')).toBeNull();
|
||||
expect(
|
||||
query('app-update-status')?.classList.contains(
|
||||
'app-update-status--stale'
|
||||
)
|
||||
).toBe(false);
|
||||
expect((query('app-update-check') as HTMLButtonElement).disabled).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps the plain check while a download of the previous channel is in flight or finished', () => {
|
||||
const form = new FormGroup({
|
||||
updateChannel: new FormControl('nightly'),
|
||||
});
|
||||
|
||||
for (const status of [
|
||||
ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloading,
|
||||
ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloaded,
|
||||
]) {
|
||||
configure(
|
||||
{ channel: 'stable', latestVersion: '0.24.0', status },
|
||||
form
|
||||
);
|
||||
|
||||
expect(query('app-update-save-and-check')).toBeNull();
|
||||
expect(query('app-update-check')).not.toBeNull();
|
||||
expect(query('app-update-channel-pending-hint')).not.toBeNull();
|
||||
}
|
||||
});
|
||||
|
||||
it('attributes a kept download to the channel it was found on, not the saved one', () => {
|
||||
const form = new FormGroup({
|
||||
updateChannel: new FormControl('nightly'),
|
||||
});
|
||||
configure(
|
||||
{
|
||||
channel: 'nightly',
|
||||
latestVersion: '0.24.0',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloaded,
|
||||
verdictChannel: 'stable',
|
||||
},
|
||||
form
|
||||
);
|
||||
|
||||
expect(query('app-update-status-channel')?.textContent?.trim()).toBe(
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_STABLE'
|
||||
);
|
||||
expect(query('app-update-channel-pending-hint')).toBeNull();
|
||||
expect(query('app-update-channel-retained-hint')).not.toBeNull();
|
||||
expect(query('app-update-save-and-check')).toBeNull();
|
||||
|
||||
// Once the new channel has been checked the badge follows it.
|
||||
configure(
|
||||
{
|
||||
channel: 'nightly',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.NotAvailable,
|
||||
verdictChannel: 'nightly',
|
||||
},
|
||||
form
|
||||
);
|
||||
|
||||
expect(query('app-update-status-channel')?.textContent?.trim()).toBe(
|
||||
'SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY'
|
||||
);
|
||||
expect(query('app-update-channel-retained-hint')).toBeNull();
|
||||
});
|
||||
|
||||
it('disables Save and check while the form cannot be saved', () => {
|
||||
const form = new FormGroup({
|
||||
updateChannel: new FormControl('nightly'),
|
||||
broken: new FormControl('', () => ({ invalid: true })),
|
||||
});
|
||||
configure({ channel: 'stable' }, form);
|
||||
|
||||
expect(
|
||||
(query('app-update-save-and-check') as HTMLButtonElement).disabled
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -4,23 +4,44 @@ import {
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import {
|
||||
AppUpdateChannel,
|
||||
APP_UPDATE_CHANNELS,
|
||||
ELECTRON_BRIDGE_APP_UPDATE_STATUSES,
|
||||
ElectronBridgeAppUpdateStatus,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { UpdateChannelOption } from './settings.models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings-about-section',
|
||||
imports: [MatButtonModule, MatIconModule, TranslateModule],
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatSelectModule,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule,
|
||||
],
|
||||
templateUrl: './settings-about-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [
|
||||
':host { display: contents; }',
|
||||
'.version-block .build-commit { opacity: 0.65; font-size: 0.85em; }',
|
||||
'.app-update-channel { margin-top: 12px; }',
|
||||
'.app-update-channel mat-form-field { width: 100%; max-width: 320px; }',
|
||||
'.app-update-channel__note { display: block; margin-top: 4px; opacity: 0.75; font-size: 0.85em; }',
|
||||
'.app-update-status__channel { align-self: flex-start; padding: 2px 9px; border-radius: 999px; font-size: 0.72rem; font-weight: 650; letter-spacing: 0.05em; text-transform: uppercase; color: var(--mat-sys-on-surface-variant); background: color-mix(in srgb, var(--mat-sys-on-surface) 9%, transparent); }',
|
||||
'.app-update-status--stale strong, .app-update-status--stale .app-update-status__channel { opacity: 0.55; }',
|
||||
],
|
||||
})
|
||||
export class SettingsAboutSectionComponent {
|
||||
@@ -28,7 +49,16 @@ export class SettingsAboutSectionComponent {
|
||||
readonly version = input<string | undefined>();
|
||||
readonly buildCommit = input<string | undefined>();
|
||||
readonly updateMessage = input<string | undefined>();
|
||||
readonly appUpdateStatus = input<ElectronBridgeAppUpdateStatus | null>(null);
|
||||
readonly appUpdateStatus = input<ElectronBridgeAppUpdateStatus | null>(
|
||||
null
|
||||
);
|
||||
/**
|
||||
* The shared settings form; the channel select lives in it so a change
|
||||
* goes through the same Save and unsaved-changes flow as every other
|
||||
* setting. Absent in hosts that only render the version block.
|
||||
*/
|
||||
readonly form = input<FormGroup | null>(null);
|
||||
readonly updateChannelOptions = input<UpdateChannelOption[]>([]);
|
||||
|
||||
readonly buildCommitShort = computed(() => {
|
||||
const commit = this.buildCommit()?.trim();
|
||||
@@ -37,11 +67,109 @@ export class SettingsAboutSectionComponent {
|
||||
});
|
||||
|
||||
readonly checkForAppUpdate = output<void>();
|
||||
/**
|
||||
* Emitted instead of a plain check while the channel select shows a
|
||||
* channel the updater has not been told about yet. The host saves the
|
||||
* form; the main process re-checks on its own once the saved channel
|
||||
* changes, so one click yields the verdict the user is looking at.
|
||||
*/
|
||||
readonly saveAndCheckForAppUpdate = output<void>();
|
||||
readonly downloadAppUpdate = output<void>();
|
||||
readonly installAppUpdate = output<void>();
|
||||
readonly openManualAppUpdate = output<void>();
|
||||
readonly openAppUpdateReleaseNotes = output<void>();
|
||||
|
||||
readonly canSelectUpdateChannel = computed(
|
||||
() =>
|
||||
this.isDesktop() &&
|
||||
this.form() !== null &&
|
||||
this.updateChannelOptions().length > 0
|
||||
);
|
||||
|
||||
/** A nightly build on the stable channel waits for the next stable release. */
|
||||
readonly isNightlyBuildOnStableChannel = computed(() => {
|
||||
const status = this.appUpdateStatus();
|
||||
|
||||
return (
|
||||
status?.installedChannel === 'nightly' &&
|
||||
status.channel === 'stable'
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* The channel the select currently shows. Read off the control on every
|
||||
* check rather than tracked as a signal: the section is eagerly checked
|
||||
* and the sibling nightly warning already reads the control the same way.
|
||||
*/
|
||||
draftUpdateChannel(): AppUpdateChannel | null {
|
||||
const value: unknown = this.form()?.get('updateChannel')?.value;
|
||||
|
||||
return APP_UPDATE_CHANNELS.includes(value as AppUpdateChannel)
|
||||
? (value as AppUpdateChannel)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True while the select shows a channel other than the one the last
|
||||
* verdict describes. The verdict on screen is then about the OTHER
|
||||
* channel, and a plain "check again" would silently repeat it.
|
||||
*/
|
||||
hasPendingChannelChange(): boolean {
|
||||
const draft = this.draftUpdateChannel();
|
||||
const applied = this.appUpdateStatus()?.channel;
|
||||
|
||||
return draft !== null && applied !== undefined && draft !== applied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether Save would make the updater re-check. A download in flight or
|
||||
* finished belongs to the previous channel and is kept, so saving there
|
||||
* changes the channel without a new verdict and the plain check stays.
|
||||
*/
|
||||
canSaveAndCheckForAppUpdate(): boolean {
|
||||
const status = this.appUpdateStatus()?.status;
|
||||
|
||||
return (
|
||||
this.hasPendingChannelChange() &&
|
||||
status !== ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloading &&
|
||||
status !== ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloaded
|
||||
);
|
||||
}
|
||||
|
||||
channelLabelKey(channel: AppUpdateChannel | null | undefined): string {
|
||||
return channel === 'nightly'
|
||||
? 'SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY'
|
||||
: 'SETTINGS.APP_UPDATE_CHANNEL_STABLE';
|
||||
}
|
||||
|
||||
/**
|
||||
* The channel the verdict on screen belongs to. `verdictChannel` is
|
||||
* stamped by every check; a channel change saved while a download was
|
||||
* running or done keeps that download, so the two can differ until
|
||||
* the new channel is checked.
|
||||
*/
|
||||
verdictChannel(): AppUpdateChannel | null {
|
||||
const status = this.appUpdateStatus();
|
||||
|
||||
return status?.verdictChannel ?? status?.channel ?? null;
|
||||
}
|
||||
|
||||
/** The kept download was found on another channel than the saved one. */
|
||||
hasRetainedOtherChannelVerdict(): boolean {
|
||||
const status = this.appUpdateStatus();
|
||||
|
||||
return (
|
||||
status?.verdictChannel !== undefined &&
|
||||
status.verdictChannel !== status.channel
|
||||
);
|
||||
}
|
||||
|
||||
readonly appliedChannelLabelKey = computed(() => {
|
||||
const channel = this.verdictChannel();
|
||||
|
||||
return channel ? this.channelLabelKey(channel) : null;
|
||||
});
|
||||
|
||||
readonly isAppUpdateBusy = computed(() => {
|
||||
const status = this.appUpdateStatus()?.status;
|
||||
|
||||
@@ -74,7 +202,7 @@ export class SettingsAboutSectionComponent {
|
||||
|
||||
return Boolean(
|
||||
status?.currentVersion &&
|
||||
status.status !== ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Checking
|
||||
status.status !== ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Checking
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Component, input, output, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
@@ -14,6 +20,8 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
],
|
||||
templateUrl: './settings-backup-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsBackupSectionComponent {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, input, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
@@ -16,6 +21,8 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
],
|
||||
templateUrl: './settings-dashboard-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsDashboardSectionComponent {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
@@ -25,6 +30,8 @@ type SettingsDeleteSummaryItem = {
|
||||
selector: 'app-settings-delete-all-playlists-dialog',
|
||||
templateUrl: './settings-delete-all-playlists-dialog.component.html',
|
||||
styleUrls: ['./settings-delete-all-playlists-dialog.component.scss'],
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
|
||||
@@ -70,6 +70,19 @@
|
||||
</div>
|
||||
|
||||
<div class="setting-item setting-item--full-control">
|
||||
<div class="setting-item__meta" data-test-id="epg-source-formats">
|
||||
<h4>{{ 'SETTINGS.EPG_SOURCE_LIST_TITLE' | translate }}</h4>
|
||||
<p>{{ 'SETTINGS.EPG_SOURCE_FORMATS_HINT' | translate }}</p>
|
||||
<p class="epg-sources__formats-examples">
|
||||
<span>{{
|
||||
'SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES' | translate
|
||||
}}</span>
|
||||
<code>https://example.org/guide.xml.gz</code>
|
||||
<code>/home/you/epg/guide.xml</code>
|
||||
<code>C:\epg\guide.xml.gz</code>
|
||||
<code>file:///…/guide.xml</code>
|
||||
</p>
|
||||
</div>
|
||||
<div class="setting-item__control">
|
||||
<ng-container formArrayName="epgUrl">
|
||||
@if (epgUrl().length === 0) {
|
||||
@@ -102,7 +115,8 @@
|
||||
}}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="url"
|
||||
type="text"
|
||||
spellcheck="false"
|
||||
[formControlName]="i"
|
||||
#epgField
|
||||
/>
|
||||
@@ -115,12 +129,31 @@
|
||||
[url]="epgField.value"
|
||||
/>
|
||||
}
|
||||
@if (canBrowseFiles()) {
|
||||
<button
|
||||
mat-icon-button
|
||||
[matTooltip]="
|
||||
'SETTINGS.BROWSE_EPG_FILE'
|
||||
| translate
|
||||
"
|
||||
[attr.aria-label]="
|
||||
'SETTINGS.BROWSE_EPG_FILE'
|
||||
| translate
|
||||
"
|
||||
type="button"
|
||||
data-test-id="epg-source-browse"
|
||||
(click)="browseEpgSource.emit(i)"
|
||||
>
|
||||
<mat-icon>folder_open</mat-icon>
|
||||
</button>
|
||||
}
|
||||
<button
|
||||
mat-icon-button
|
||||
[matTooltip]="
|
||||
'SETTINGS.REFRESH_EPG' | translate
|
||||
"
|
||||
type="button"
|
||||
data-test-id="epg-source-refresh"
|
||||
[disabled]="epgField.value === ''"
|
||||
(click)="refreshEpg.emit(epgUrl().value[i])"
|
||||
>
|
||||
@@ -132,6 +165,7 @@
|
||||
'SETTINGS.REMOVE_EPG_SOURCE' | translate
|
||||
"
|
||||
type="button"
|
||||
data-test-id="epg-source-remove"
|
||||
(click)="removeEpgSource.emit(i)"
|
||||
>
|
||||
<mat-icon>remove</mat-icon>
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Component, input, output, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { FormArray, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
@@ -28,15 +34,19 @@ import { EpgViewModeOption } from './settings.models';
|
||||
],
|
||||
templateUrl: './settings-epg-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsEpgSectionComponent {
|
||||
readonly form = input.required<FormGroup>();
|
||||
readonly epgUrl = input.required<FormArray>();
|
||||
readonly isClearingEpgData = input(false);
|
||||
readonly canBrowseFiles = input(false);
|
||||
readonly epgViewModeOptions = input.required<EpgViewModeOption[]>();
|
||||
|
||||
readonly refreshEpg = output<string>();
|
||||
readonly browseEpgSource = output<number>();
|
||||
readonly removeEpgSource = output<number>();
|
||||
readonly addEpgSource = output<void>();
|
||||
readonly refreshAllEpg = output<void>();
|
||||
|
||||
@@ -174,4 +174,50 @@ describe('SettingsEpgFacade', () => {
|
||||
|
||||
expect(epgService.fetchEpg).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('browse', () => {
|
||||
it('writes the picked file into the row and dirties the form', async () => {
|
||||
formFacade.setEpgUrls([
|
||||
'https://epg.example.org/guide.xml',
|
||||
'https://epg.example.org/old.xml',
|
||||
]);
|
||||
(epgBridge.pickEpgFile as jest.Mock).mockResolvedValue(
|
||||
'/home/user/epg/guide.xml.gz'
|
||||
);
|
||||
|
||||
await facade.browse(1);
|
||||
|
||||
expect(formFacade.epgUrl.at(1).value).toBe(
|
||||
'/home/user/epg/guide.xml.gz'
|
||||
);
|
||||
expect(formFacade.epgUrl.at(0).value).toBe(
|
||||
'https://epg.example.org/guide.xml'
|
||||
);
|
||||
expect(formFacade.epgUrl.at(1).dirty).toBe(true);
|
||||
expect(formFacade.form.dirty).toBe(true);
|
||||
});
|
||||
|
||||
it('leaves the row untouched when the picker is cancelled', async () => {
|
||||
formFacade.setEpgUrls(['https://epg.example.org/guide.xml']);
|
||||
(epgBridge.pickEpgFile as jest.Mock).mockResolvedValue(null);
|
||||
|
||||
await facade.browse(0);
|
||||
|
||||
expect(formFacade.epgUrl.at(0).value).toBe(
|
||||
'https://epg.example.org/guide.xml'
|
||||
);
|
||||
expect(formFacade.form.dirty).toBe(false);
|
||||
});
|
||||
|
||||
it('never opens the picker without the capability or for a missing row', async () => {
|
||||
formFacade.setEpgUrls(['https://epg.example.org/guide.xml']);
|
||||
await facade.browse(5);
|
||||
expect(epgBridge.pickEpgFile).not.toHaveBeenCalled();
|
||||
|
||||
(epgBridge as { supportsFilePicker: boolean }).supportsFilePicker =
|
||||
false;
|
||||
await facade.browse(0);
|
||||
expect(epgBridge.pickEpgFile).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -25,6 +25,29 @@ export class SettingsEpgFacade {
|
||||
|
||||
readonly isClearing = signal(false);
|
||||
|
||||
get supportsFilePicker(): boolean {
|
||||
return this.epgBridge.supportsFilePicker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Native picker for a local XMLTV file. The chosen absolute path becomes
|
||||
* the value of row `index`; a cancelled dialog leaves the row untouched.
|
||||
*/
|
||||
async browse(index: number): Promise<void> {
|
||||
const control = this.formFacade.epgUrl.at(index);
|
||||
if (!control || !this.epgBridge.supportsFilePicker) {
|
||||
return;
|
||||
}
|
||||
const filePath = await this.epgBridge.pickEpgFile();
|
||||
if (!filePath) {
|
||||
return;
|
||||
}
|
||||
control.setValue(filePath);
|
||||
control.markAsDirty();
|
||||
control.markAsTouched();
|
||||
this.formFacade.form.markAsDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Force-fetch EPG for a single URL, bypassing the 12-hour freshness check.
|
||||
* The plain fetchEpg would short-circuit on fresh data and click the
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { Settings, VideoPlayer } from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
createEpgUrlControl,
|
||||
createSettingsForm,
|
||||
createSettingsFromFormValue,
|
||||
} from './settings-form.utils';
|
||||
@@ -81,6 +82,38 @@ describe('settings form utils — startup window mode', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('settings form utils — update channel', () => {
|
||||
const formBuilder = new FormBuilder();
|
||||
|
||||
it('defaults the form control to the stable channel', () => {
|
||||
const form = createSettingsForm(formBuilder, true);
|
||||
|
||||
expect(form.getRawValue().updateChannel).toBe('stable');
|
||||
});
|
||||
|
||||
it('carries the chosen channel into the settings object', () => {
|
||||
const form = createSettingsForm(formBuilder, true);
|
||||
form.patchValue({ updateChannel: 'nightly' });
|
||||
|
||||
expect(
|
||||
createSettingsFromFormValue(form, {} as Settings).updateChannel
|
||||
).toBe('nightly');
|
||||
});
|
||||
|
||||
it('collapses a missing or unknown channel to stable', () => {
|
||||
const form = createSettingsForm(formBuilder, true);
|
||||
form.patchValue({ updateChannel: null as unknown as 'stable' });
|
||||
expect(
|
||||
createSettingsFromFormValue(form, {} as Settings).updateChannel
|
||||
).toBe('stable');
|
||||
|
||||
form.patchValue({ updateChannel: 'canary' as unknown as 'stable' });
|
||||
expect(
|
||||
createSettingsFromFormValue(form, {} as Settings).updateChannel
|
||||
).toBe('stable');
|
||||
});
|
||||
});
|
||||
|
||||
describe('settings form utils — EPG display offset', () => {
|
||||
const formBuilder = new FormBuilder();
|
||||
|
||||
@@ -202,3 +235,24 @@ describe('settings form utils — embedded MPV session options', () => {
|
||||
expect(settings.embeddedMpvAutoReconnect).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('settings form utils — EPG source control', () => {
|
||||
it.each([
|
||||
'https://epg.example.org/guide.xml.gz',
|
||||
'/home/user/epg/guide.xml',
|
||||
'C:\\epg\\guide.xml.gz',
|
||||
'file:///home/user/epg/guide.xml',
|
||||
'',
|
||||
])('accepts %j', (value) => {
|
||||
expect(createEpgUrlControl(value).valid).toBe(true);
|
||||
});
|
||||
|
||||
it.each(['guide.xml', 'ftp://epg.example.org/guide.xml', 'epg'])(
|
||||
'rejects %j',
|
||||
(value) => {
|
||||
const control = createEpgUrlControl(value);
|
||||
expect(control.valid).toBe(false);
|
||||
expect(control.errors).toEqual({ epgSourceReference: true });
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -7,11 +7,14 @@ import {
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
import {
|
||||
AppUpdateChannel,
|
||||
CoverSize,
|
||||
DEFAULT_APP_UPDATE_CHANNEL,
|
||||
DEFAULT_DASHBOARD_RAILS_SETTINGS,
|
||||
DEFAULT_TMDB_SETTINGS,
|
||||
EpgViewMode,
|
||||
Language,
|
||||
normalizeAppUpdateChannel,
|
||||
normalizeDashboardRailsSettings,
|
||||
normalizeEmbeddedMpvExtraOptions,
|
||||
normalizeExternalPlayerArguments,
|
||||
@@ -24,11 +27,10 @@ import {
|
||||
StreamFormat,
|
||||
Theme,
|
||||
validateEmbeddedMpvExtraOptions,
|
||||
validateEpgSourceReferenceControl,
|
||||
VideoPlayer,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
|
||||
export const EPG_URL_PATTERN = /^(http|https|file):\/\/[^ "]+$/;
|
||||
|
||||
/**
|
||||
* Rejects malformed lines and the option keys the embed depends on, so a
|
||||
* value that the main process would have to drop never gets saved.
|
||||
@@ -45,8 +47,13 @@ export function embeddedMpvExtraOptionsValidator(
|
||||
return validateEmbeddedMpvExtraOptions(control.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* An EPG source is a remote XMLTV link or a local file (absolute path or
|
||||
* `file:` URL); the accepted shapes live in `@iptvnator/shared/interfaces`
|
||||
* so the playlist dialog validates identically.
|
||||
*/
|
||||
export function createEpgUrlControl(value = ''): FormControl<string | null> {
|
||||
return new FormControl(value, [Validators.pattern(EPG_URL_PATTERN)]);
|
||||
return new FormControl(value, [validateEpgSourceReferenceControl]);
|
||||
}
|
||||
|
||||
export function createSettingsForm(
|
||||
@@ -86,6 +93,7 @@ export function createSettingsForm(
|
||||
}),
|
||||
startupBehavior: StartupBehavior.FirstView,
|
||||
startupWindowMode: 'normal' as StartupWindowMode,
|
||||
updateChannel: DEFAULT_APP_UPDATE_CHANNEL as AppUpdateChannel,
|
||||
showExternalPlaybackBar: true,
|
||||
stripCountryPrefix: false,
|
||||
theme: Theme.SystemTheme,
|
||||
@@ -111,6 +119,7 @@ export function createSettingsForm(
|
||||
embeddedMpvAutoReconnect: true,
|
||||
portalConnectivityGuard: true,
|
||||
coverSize: 'medium' as CoverSize,
|
||||
showCoverTitles: true,
|
||||
...(supportsEpg
|
||||
? {
|
||||
preferUploadedEpgOverXtream: false,
|
||||
@@ -178,6 +187,7 @@ export function createSettingsFromFormValue(
|
||||
dashboardRails: normalizeDashboardRailsSettings(value.dashboardRails),
|
||||
startupBehavior: value.startupBehavior ?? StartupBehavior.FirstView,
|
||||
startupWindowMode: normalizeStartupWindowMode(value.startupWindowMode),
|
||||
updateChannel: normalizeAppUpdateChannel(value.updateChannel),
|
||||
showExternalPlaybackBar: value.showExternalPlaybackBar ?? true,
|
||||
stripCountryPrefix: value.stripCountryPrefix ?? false,
|
||||
theme: value.theme ?? Theme.SystemTheme,
|
||||
@@ -201,6 +211,7 @@ export function createSettingsFromFormValue(
|
||||
embeddedMpvAutoReconnect: value.embeddedMpvAutoReconnect ?? true,
|
||||
portalConnectivityGuard: value.portalConnectivityGuard !== false,
|
||||
coverSize: value.coverSize ?? 'medium',
|
||||
showCoverTitles: value.showCoverTitles !== false,
|
||||
epgUrl,
|
||||
preferUploadedEpgOverXtream:
|
||||
value.preferUploadedEpgOverXtream ??
|
||||
|
||||
@@ -108,6 +108,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-item__meta">
|
||||
<h4 id="cover-titles-label">
|
||||
{{ 'SETTINGS.COVER_TITLES' | translate }}
|
||||
</h4>
|
||||
<p id="cover-titles-description">
|
||||
{{ 'SETTINGS.COVER_TITLES_DESCRIPTION' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="setting-item__control setting-item__toggle">
|
||||
<mat-checkbox
|
||||
formControlName="showCoverTitles"
|
||||
aria-labelledby="cover-titles-label"
|
||||
aria-describedby="cover-titles-description"
|
||||
data-test-id="cover-titles-toggle"
|
||||
></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-item__meta">
|
||||
<h4>{{ 'SETTINGS.STARTUP_BEHAVIOR' | translate }}</h4>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, input, output, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
@@ -27,6 +33,8 @@ import {
|
||||
],
|
||||
templateUrl: './settings-general-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsGeneralSectionComponent {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
StartupBehaviorOption,
|
||||
StartupWindowModeOption,
|
||||
ThemeOption,
|
||||
UpdateChannelOption,
|
||||
} from './settings.models';
|
||||
|
||||
export const SETTINGS_THEME_OPTIONS: ThemeOption[] = [
|
||||
@@ -90,6 +91,17 @@ export const SETTINGS_STARTUP_WINDOW_MODE_OPTIONS: StartupWindowModeOption[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const SETTINGS_UPDATE_CHANNEL_OPTIONS: UpdateChannelOption[] = [
|
||||
{
|
||||
value: 'stable',
|
||||
labelKey: 'SETTINGS.APP_UPDATE_CHANNEL_STABLE',
|
||||
},
|
||||
{
|
||||
value: 'nightly',
|
||||
labelKey: 'SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY',
|
||||
},
|
||||
];
|
||||
|
||||
export const SETTINGS_OS_PLAYER_OPTIONS: SettingsPlayerOption[] = [
|
||||
{
|
||||
id: VideoPlayer.MPV,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, input, output, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
@@ -30,6 +36,8 @@ import { SettingsPlayerOption } from './settings.models';
|
||||
],
|
||||
templateUrl: './settings-playback-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsPlaybackSectionComponent {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Component, input, output, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
@@ -24,6 +30,8 @@ import { QRCodeComponent } from 'angularx-qrcode';
|
||||
],
|
||||
templateUrl: './settings-remote-control-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsRemoteControlSectionComponent {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Component, input, output, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
@@ -15,6 +21,8 @@ import { SettingsPlaylistDeleteSummary } from './settings.models';
|
||||
],
|
||||
templateUrl: './settings-reset-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsResetSectionComponent {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
input,
|
||||
signal,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
@@ -32,6 +33,8 @@ type TmdbKeyTestState = 'idle' | 'testing' | 'success' | 'error';
|
||||
],
|
||||
templateUrl: './settings-tmdb-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
styles: [
|
||||
`
|
||||
app-settings-tmdb-section {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, inject, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
@@ -38,6 +38,8 @@ export interface SettingsUnsavedChangesDialogData {
|
||||
}
|
||||
`,
|
||||
],
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
template: `
|
||||
<h2 mat-dialog-title>
|
||||
{{ 'SETTINGS.UNSAVED_DIALOG_TITLE' | translate }}
|
||||
|
||||
@@ -81,6 +81,29 @@ describe('SettingsComponent form', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('stages the posters-only cover wall until Save', async () => {
|
||||
const checkbox = (fixture.nativeElement as HTMLElement).querySelector(
|
||||
'[data-test-id="cover-titles-toggle"] input'
|
||||
) as HTMLInputElement;
|
||||
expect(checkbox).not.toBeNull();
|
||||
expect(checkbox.checked).toBe(true);
|
||||
|
||||
checkbox.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.settingsForm.get('showCoverTitles')?.value).toBe(
|
||||
false
|
||||
);
|
||||
expect(settingsStore.updateSettings).not.toHaveBeenCalled();
|
||||
|
||||
await component.form.save(() => undefined);
|
||||
|
||||
expect(settingsStore.updateSettings).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ showCoverTitles: false }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
it('hides the portal pause setting when the desktop bridge is unavailable', () => {
|
||||
fixture.destroy();
|
||||
window.electron = undefined;
|
||||
@@ -455,7 +478,11 @@ describe('SettingsComponent form', () => {
|
||||
const webFixture = TestBed.createComponent(SettingsComponent);
|
||||
const webComponent = webFixture.componentInstance;
|
||||
stubSettingsSideEffects(webComponent);
|
||||
const loadSettings = jest.spyOn(webComponent.form, 'loadSettings');
|
||||
webFixture.detectChanges();
|
||||
expect(loadSettings).toHaveBeenCalledTimes(1);
|
||||
// Await hydration before editing; Zone cannot track native awaits.
|
||||
await loadSettings.mock.results[0].value;
|
||||
await webFixture.whenStable();
|
||||
|
||||
webComponent.settingsForm.patchValue({ theme: Theme.DarkTheme });
|
||||
|
||||
@@ -40,8 +40,10 @@
|
||||
[form]="settingsForm"
|
||||
[epgUrl]="form.epgUrl"
|
||||
[isClearingEpgData]="epg.isClearing()"
|
||||
[canBrowseFiles]="epg.supportsFilePicker"
|
||||
[epgViewModeOptions]="epgViewModeOptions"
|
||||
(refreshEpg)="epg.refresh($event)"
|
||||
(browseEpgSource)="epg.browse($event)"
|
||||
(removeEpgSource)="form.removeEpgSource($event)"
|
||||
(addEpgSource)="form.addEpgSource()"
|
||||
(refreshAllEpg)="epg.refreshAll()"
|
||||
@@ -107,11 +109,14 @@
|
||||
@case ('about') {
|
||||
<app-settings-about-section
|
||||
[isDesktop]="isDesktop"
|
||||
[form]="settingsForm"
|
||||
[updateChannelOptions]="updateChannelOptions"
|
||||
[version]="appUpdate.version()"
|
||||
[buildCommit]="buildCommit"
|
||||
[updateMessage]="appUpdate.updateMessage()"
|
||||
[appUpdateStatus]="appUpdate.status()"
|
||||
(checkForAppUpdate)="appUpdate.checkForAppUpdate()"
|
||||
(saveAndCheckForAppUpdate)="onSubmit()"
|
||||
(downloadAppUpdate)="appUpdate.downloadAppUpdate()"
|
||||
(installAppUpdate)="appUpdate.installAppUpdate()"
|
||||
(openManualAppUpdate)="appUpdate.openManualAppUpdate()"
|
||||
|
||||
@@ -303,6 +303,22 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.epg-sources__formats-examples {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px 8px;
|
||||
|
||||
code {
|
||||
padding: 1px 6px;
|
||||
border-radius: 6px;
|
||||
background: var(--app-hover-overlay);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.epg-sources-empty {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
@@ -78,6 +78,10 @@ describe('SettingsComponent', () => {
|
||||
const lifecycleFixture = TestBed.createComponent(SettingsComponent);
|
||||
const lifecycleComponent = lifecycleFixture.componentInstance;
|
||||
stubSettingsSideEffects(lifecycleComponent);
|
||||
const loadSettings = jest.spyOn(
|
||||
lifecycleComponent.form,
|
||||
'loadSettings'
|
||||
);
|
||||
const appUpdateInit = jest.spyOn(lifecycleComponent.appUpdate, 'init');
|
||||
const appUpdateDispose = jest.spyOn(
|
||||
lifecycleComponent.appUpdate,
|
||||
@@ -89,6 +93,9 @@ describe('SettingsComponent', () => {
|
||||
);
|
||||
|
||||
lifecycleFixture.detectChanges();
|
||||
expect(loadSettings).toHaveBeenCalledTimes(1);
|
||||
// Let ngOnInit resume after its native promise, which Zone cannot track.
|
||||
await loadSettings.mock.results[0].value;
|
||||
await lifecycleFixture.whenStable();
|
||||
|
||||
expect(appUpdateInit).toHaveBeenCalledTimes(1);
|
||||
@@ -197,7 +204,10 @@ describe('SettingsComponent', () => {
|
||||
|
||||
it('save-and-leave persists before allowing the navigation', async () => {
|
||||
component.settingsForm.markAsDirty();
|
||||
jest.spyOn(component.epg, 'fetchConfiguredEpg').mockImplementation();
|
||||
jest.spyOn(
|
||||
component.epg,
|
||||
'fetchConfiguredEpg'
|
||||
).mockImplementation();
|
||||
answerDialogWith('save');
|
||||
|
||||
await expect(
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewEncapsulation,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
@@ -41,6 +42,7 @@ import {
|
||||
SETTINGS_EPG_VIEW_MODE_OPTIONS,
|
||||
SETTINGS_STARTUP_BEHAVIOR_OPTIONS,
|
||||
SETTINGS_STARTUP_WINDOW_MODE_OPTIONS,
|
||||
SETTINGS_UPDATE_CHANNEL_OPTIONS,
|
||||
SETTINGS_THEME_OPTIONS,
|
||||
} from './settings-options';
|
||||
import { SettingsParentalLockFacade } from './settings-parental-lock.facade';
|
||||
@@ -96,6 +98,8 @@ export const SETTINGS_DEFAULT_SECTION = 'general';
|
||||
SettingsResetSectionComponent,
|
||||
SettingsTmdbSectionComponent,
|
||||
],
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
providers: [
|
||||
SettingsAppUpdateFacade,
|
||||
SettingsBackupFacade,
|
||||
@@ -171,6 +175,7 @@ export class SettingsComponent
|
||||
readonly coverSizeOptions = SETTINGS_COVER_SIZE_OPTIONS;
|
||||
readonly startupBehaviorOptions = SETTINGS_STARTUP_BEHAVIOR_OPTIONS;
|
||||
readonly startupWindowModeOptions = SETTINGS_STARTUP_WINDOW_MODE_OPTIONS;
|
||||
readonly updateChannelOptions = SETTINGS_UPDATE_CHANNEL_OPTIONS;
|
||||
readonly epgViewModeOptions = SETTINGS_EPG_VIEW_MODE_OPTIONS;
|
||||
|
||||
readonly sectionNavItems: SettingsSection[] = buildSettingsSectionNavItems({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
AppUpdateChannel,
|
||||
CoverSize,
|
||||
EpgViewMode,
|
||||
StartupBehavior,
|
||||
@@ -30,6 +31,11 @@ export interface StartupWindowModeOption {
|
||||
labelKey: string;
|
||||
}
|
||||
|
||||
export interface UpdateChannelOption {
|
||||
value: AppUpdateChannel;
|
||||
labelKey: string;
|
||||
}
|
||||
|
||||
export interface CoverSizeOption {
|
||||
value: CoverSize;
|
||||
icon: string;
|
||||
|
||||
@@ -97,6 +97,7 @@ export const DEFAULT_SETTINGS = {
|
||||
showDashboard: true,
|
||||
startupBehavior: StartupBehavior.FirstView,
|
||||
startupWindowMode: 'normal',
|
||||
updateChannel: 'stable',
|
||||
showExternalPlaybackBar: true,
|
||||
stripCountryPrefix: false,
|
||||
theme: Theme.SystemTheme,
|
||||
@@ -115,6 +116,7 @@ export const DEFAULT_SETTINGS = {
|
||||
embeddedMpvAutoReconnect: true,
|
||||
portalConnectivityGuard: true,
|
||||
coverSize: 'medium',
|
||||
showCoverTitles: true,
|
||||
dashboardRails: DEFAULT_DASHBOARD_RAILS,
|
||||
preferUploadedEpgOverXtream: false,
|
||||
epgViewMode: 'timeline',
|
||||
@@ -127,6 +129,8 @@ export const DEFAULT_APP_UPDATE_STATUS: ElectronBridgeAppUpdateStatus = {
|
||||
manualDownloadUrl: 'https://github.com/4gray/iptvnator/releases/latest',
|
||||
status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Idle,
|
||||
supportedSelfUpdate: true,
|
||||
channel: 'stable',
|
||||
installedChannel: 'stable',
|
||||
};
|
||||
|
||||
export class MatSnackBarStub {
|
||||
@@ -216,7 +220,9 @@ export function createEpgBridgeStub(): Partial<EpgRuntimeBridgeService> {
|
||||
clearEpgData: jest.fn().mockResolvedValue({ success: true }),
|
||||
clearEpgDataForSource: jest.fn().mockResolvedValue({ success: true }),
|
||||
forceFetchEpg: jest.fn().mockResolvedValue({ success: true }),
|
||||
pickEpgFile: jest.fn().mockResolvedValue(null),
|
||||
supportsDataManagement: true,
|
||||
supportsFilePicker: true,
|
||||
supportsImport: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "تحديث EPG",
|
||||
"REFRESH_ALL_EPG": "تحديث الكل",
|
||||
"REMOVE_EPG_SOURCE": "إزالة مصدر EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "محدث",
|
||||
"EPG_STATUS_STALE": "يحتاج تحديث",
|
||||
"EPG_STATUS_ERROR": "فشل",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "فشل مسح بيانات EPG. يرجى المحاولة مرة أخرى.",
|
||||
"CLEARING_EPG_DATA": "جارٍ المسح…",
|
||||
"EPG_URL_ERROR": "القيمة ليست رابطًا صالحًا",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "اختر مشغل الفيديو الافتراضي",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "عناصر تحكم موحّدة لمشغلات الويب",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "استخدم عناصر التحكم المشتركة في IPTVnator مع HTML5 وVideo.js وArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "تعبئة المساحة حول المشغل بنسخة مشوشة ومعتمة من الملصق بدلاً من الخطوط السوداء.",
|
||||
"PLAYER_UP_NEXT_RAIL": "شريط الحلقات التالية",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "في النوافذ العريضة، تثبيت مشغل المسلسل على اليسار وعرض الحلقات القادمة بجانبه.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "قائمة القنوات في وضع ملء الشاشة",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "في وضع ملء الشاشة، حرّك الماوس إلى الحافة اليسرى أو اضغط C لإظهار قائمة القنوات وتبديل القناة دون الخروج من ملء الشاشة.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "قائمة القنوات والحلقات في وضع ملء الشاشة",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "ضع الماوس على الحافة اليسرى أو انقر عليها أو اضغط C في وضع ملء الشاشة لإظهار قائمة قنوات البث المباشر أو قائمة حلقات المسلسل والتبديل دون مغادرة وضع ملء الشاشة.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "اختر تنسيق البث الافتراضي (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "اختر لغة التطبيق",
|
||||
"THEME_DESCRIPTION": "اختر السمة البصرية للتطبيق",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "صغير",
|
||||
"COVER_SIZE_MEDIUM": "متوسط",
|
||||
"COVER_SIZE_LARGE": "كبير",
|
||||
"COVER_TITLES": "عرض العناوين أسفل الأغلفة",
|
||||
"COVER_TITLES_DESCRIPTION": "عطّل هذا الخيار للحصول على جدار ملصقات فقط في شبكات الأفلام والمسلسلات: يظهر العنوان على الغلاف عند تمرير المؤشر فوقه أو التركيز عليه. تحتفظ القنوات المباشرة ونتائج البحث وأشرطة لوحة التحكم بعناوينها.",
|
||||
"EPG_VIEW_MODE": "عرض الدليل",
|
||||
"EPG_OFFSET_TITLE": "إزاحة وقت دليل البرامج",
|
||||
"EPG_OFFSET_HINT": "اضبط أوقات البرامج المعروضة دون تحديث الدليل. استخدم الدقائق من -720 إلى 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "إعادة تشغيل التطبيق",
|
||||
"APP_UPDATE_OPEN_RELEASE": "فتح إصدار GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "ما الجديد",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "لم يتم العثور على ملاحظات الإصدار {{version}} على GitHub. ربما تمت إزالة الإصدار أو لم يُنشر بعد.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "تعذّر تحميل ملاحظات الإصدار.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "فتح صفحة الإصدارات",
|
||||
"APP_UPDATE_CHANNEL": "قناة التحديث",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "تتبع Stable الإصدارات المنشورة. تتبع Nightly كل دمج في master وتثبّت الإصدارات التجريبية تلقائيًا. يُطبَّق عند الحفظ.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "حفظ والتحقق من تحديثات {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "هذه النتيجة تخص قناة {{current}}. احفظ للتبديل إلى {{next}} ثم تحقق مرة أخرى.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "تم العثور على هذا التحديث في قناة {{current}}. لم يتم التحقق من قناة {{next}} بعد.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "إصدارات Nightly هي لقطات غير مختبرة من master. قد تتعطل، وتغييراتها في قاعدة البيانات دائمة: عند العودة إلى Stable يبقى هذا الإصدار مثبتًا حتى يصدر إصدار مستقر أحدث. أنشئ نسخة احتياطية من قوائم التشغيل أولًا.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "أنت تستخدم إصدار Nightly. ستقترح قناة Stable الإصدار المستقر التالي بمجرد أن يصبح أحدث من هذا الإصدار.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "MPV المدمج: محرك نسخ الإطارات (تجريبي)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "يُرسم الفيديو على لوحة التطبيق بحيث تظهر القوائم ومربعات الحوار فوقه.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "يسري المفعول بعد إعادة تشغيل التطبيق.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "الخروج من ملء الشاشة",
|
||||
"CHANNEL_LIST": "قائمة القنوات",
|
||||
"HIDE_CHANNEL_LIST": "إخفاء قائمة القنوات",
|
||||
"EPISODE_LIST": "قائمة الحلقات",
|
||||
"HIDE_EPISODE_LIST": "إخفاء قائمة الحلقات",
|
||||
"SEARCH_CHANNELS": "البحث عن القنوات",
|
||||
"SEARCH_IN": "البحث في {{title}}",
|
||||
"CLEAR_SEARCH": "مسح البحث",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "برامج",
|
||||
"LOADING": "جارٍ التحميل",
|
||||
"QUEUED": "في قائمة الانتظار",
|
||||
"IMPORT_PANEL_MINIMIZE": "تصغير لوحة استيراد دليل البرامج",
|
||||
"IMPORT_PANEL_EXPAND": "توسيع لوحة استيراد دليل البرامج",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} قيد التحميل",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} في قائمة الانتظار",
|
||||
"TOTAL_CHANNELS": "إجمالي القنوات",
|
||||
"TOTAL_PROGRAMS": "إجمالي البرامج",
|
||||
"UP_TO_DATE": "EPG محدث، لا حاجة للمزامنة",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "تم وضع علامة على المسلسل كغير مشاهد",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "فشل تحديث المسلسل",
|
||||
"SERIES_WATCH_MENU": "إجراءات مشاهدة المسلسل",
|
||||
"MOVIE_MARKED_WATCHED": "تم وضع علامة كمشاهد",
|
||||
"MOVIE_MARKED_UNWATCHED": "تم وضع علامة كغير مشاهد",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "فشل تحديث حالة المشاهدة",
|
||||
"EPISODES_WATCHED": "تمت مشاهدة {{count}} من {{total}} حلقة",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "البيانات الوصفية للبوابة غير متاحة",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "نبذة",
|
||||
"SEASONS_AND_EPISODES": "المواسم والحلقات",
|
||||
"SEASON_TAB": "الموسم {{number}}",
|
||||
"EPISODE_COUNT_ONE": "حلقة واحدة",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} حلقات",
|
||||
"BACK_TO_PLAYING_EPISODE": "العودة إلى الحلقة قيد التشغيل",
|
||||
"GRID_VIEW": "عرض شبكي",
|
||||
"LIST_VIEW": "عرض قائمة",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "هذا المحتوى غير متاح على هذا الخادم",
|
||||
"PLAYBACK_ERROR": "فشل الحصول على رابط التشغيل",
|
||||
"LOADING_SEASONS": "جارٍ تحميل المواسم...",
|
||||
"LOADING_EPISODES": "جارٍ تحميل الحلقات...",
|
||||
"EPISODES_LOAD_FAILED": "تعذر تحميل الحلقات.",
|
||||
"LOADING_ALL_CHANNELS": "جارٍ تحميل قائمة القنوات الكاملة…",
|
||||
"REFRESH_CHANNEL_LIST": "تحديث قائمة القنوات",
|
||||
"NO_CHANNELS_IN_CATEGORY": "لا توجد قنوات في هذه الفئة",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "فتح العناصر المشاهدة مؤخرًا",
|
||||
"SUBMIT_SHELL_SEARCH": "إرسال بحث مساحة العمل",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "تبديل ملء الشاشة لنافذة التطبيق",
|
||||
"ZOOM_IN_OUT": "تكبير التطبيق أو تصغيره",
|
||||
"RESET_ZOOM": "إعادة ضبط تكبير التطبيق",
|
||||
"TOGGLE_SIDEBAR": "تبديل الشريط الجانبي",
|
||||
"M3U_CHANNEL_NUMBER": "اختيار قناة M3U حسب الرقم",
|
||||
"PLAY_PAUSE": "تشغيل أو إيقاف تشغيل Embedded MPV مؤقتًا",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "حدث EPG",
|
||||
"REFRESH_ALL_EPG": "حدث الكل",
|
||||
"REMOVE_EPG_SOURCE": "زول مصدر EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "محدث",
|
||||
"EPG_STATUS_STALE": "خاصو يتحدث",
|
||||
"EPG_STATUS_ERROR": "فشل",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "فشل مسح بيانات EPG. عفاك حاول مرة أخرى.",
|
||||
"CLEARING_EPG_DATA": "جاري المسح…",
|
||||
"EPG_URL_ERROR": "القيمة ماشي رابط صحيح",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "اختار مشغل الفيديو الافتراضي",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "عناصر تحكم موحدة لمشغلات الويب",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "استعمل عناصر التحكم المشتركة ديال IPTVnator مع HTML5 وVideo.js وArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "ملي المساحة حول المشغل بغلاف معتم وطابت بلاصة الشرائط السوداء.",
|
||||
"PLAYER_UP_NEXT_RAIL": "شريط الحلقات الجاية",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "فالنوافذ العريضة، حط مشغل السلسلة على اليسار وبيّن الحلقات الجاية بحداه.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "لائحة القنوات فوضع ملء الشاشة",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "فوضع ملء الشاشة، حرّك الماوس للحافة اليسرى ولا ورّك على C باش تبان لائحة القنوات وتبدّل القناة بلا ما تخرج من ملء الشاشة.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "لائحة القنوات والحلقات فوضع ملء الشاشة",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "حط الفارة فالحافة اليسرى ولا كليكي عليها ولا ضغط C فوضع ملء الشاشة باش تبان لائحة القنوات ديال البث المباشر ولا لائحة الحلقات ديال المسلسل وتبدل بلا ما تخرج من ملء الشاشة.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "اختار صيغة البث الافتراضية (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "اختار لغة التطبيق",
|
||||
"THEME_DESCRIPTION": "اختار المظهر المرئي للتطبيق",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "صغير",
|
||||
"COVER_SIZE_MEDIUM": "متوسط",
|
||||
"COVER_SIZE_LARGE": "كبير",
|
||||
"COVER_TITLES": "وري العناوين تحت الأغلفة",
|
||||
"COVER_TITLES_DESCRIPTION": "طفي هاد الخيار باش تولي شبكات الأفلام والسلاسل حيط غير بالأغلفة: العنوان كيبان فوق الغلاف ملي تقرب بالماوس عليه ولا تفوكيسي عليه بالكيبورد. القنوات المباشرة، نتايج البحث وقضبان لوحة التحكم كيبقاو عناوينهم ظاهرين.",
|
||||
"EPG_VIEW_MODE": "عرض الدليل",
|
||||
"EPG_OFFSET_TITLE": "إزاحة وقت دليل البرامج",
|
||||
"EPG_OFFSET_HINT": "اضبط أوقات البرامج المعروضة دون تحديث الدليل. استخدم الدقائق من -720 إلى 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "عاود شعل التطبيق",
|
||||
"APP_UPDATE_OPEN_RELEASE": "حل إصدار GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "شنو الجديد",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "ما لقيناش ملاحظات الإصدار {{version}} فـ GitHub. يمكن الإصدار تحيد ولا مازال ما تنشرش.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "ما قدرناش نحمّلو ملاحظات الإصدار.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "فتح صفحة الإصدارات",
|
||||
"APP_UPDATE_CHANNEL": "قناة التحديث",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable كتبع الإصدارات المنشورة. Nightly كتبع كل دمج ف master وكتثبّت الإصدارات التجريبية أوتوماتيكيا. كيتطبّق ملي تحفظ.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "حفظ وتحقق من تحديثات {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "هاد النتيجة ديال قناة {{current}}. حفظ باش تبدل لـ {{next}} وعاود تحقق.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "هاد التحديث تلقا فقناة {{current}}. قناة {{next}} مازال ما تحققناش منها.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "إصدارات Nightly هي لقطات من master ما مجرّباش. يمكن يطيحو، والتغييرات ديالهم ف قاعدة البيانات دائمة: ملي ترجع ل Stable هاد الإصدار كيبقى مثبّت حتى يخرج إصدار مستقر جديد. دير نسخة احتياطية ديال قوائم التشغيل قبل.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "راك كتستعمل إصدار Nightly. قناة Stable غادي تقترح الإصدار المستقر الجاي ملي يولي جديد كثر من هاد الإصدار.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "MPV المدمج: محرك نسخ الإطارات (تجريبي)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "الفيديو كيترسم على لوحة التطبيق باش القوائم والحوارات يبانو فوقو.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "غادي يخدم من بعد ما تعاود تشغل التطبيق.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "خرج من ملء الشاشة",
|
||||
"CHANNEL_LIST": "لائحة القنوات",
|
||||
"HIDE_CHANNEL_LIST": "خبّي لائحة القنوات",
|
||||
"EPISODE_LIST": "لائحة الحلقات",
|
||||
"HIDE_EPISODE_LIST": "خبّي لائحة الحلقات",
|
||||
"SEARCH_CHANNELS": "قلّب على القنوات",
|
||||
"SEARCH_IN": "قلّب ف {{title}}",
|
||||
"CLEAR_SEARCH": "مسح البحث",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "برامج",
|
||||
"LOADING": "جاري التشريج",
|
||||
"QUEUED": "في الطابور",
|
||||
"IMPORT_PANEL_MINIMIZE": "صغّر لوحة استيراد دليل البرامج",
|
||||
"IMPORT_PANEL_EXPAND": "كبّر لوحة استيراد دليل البرامج",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} كيتحمّل",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} فالصف",
|
||||
"TOTAL_CHANNELS": "مجموع القنوات",
|
||||
"TOTAL_PROGRAMS": "مجموع البرامج",
|
||||
"UP_TO_DATE": "EPG محدث، ما خاصش مزامنة",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "السلسلة تعلمات كما متشافاش",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "فشل تحديث السلسلة",
|
||||
"SERIES_WATCH_MENU": "إجراءات مشاهدة السلسلة",
|
||||
"MOVIE_MARKED_WATCHED": "تعلم كمتشاف",
|
||||
"MOVIE_MARKED_UNWATCHED": "تعلم كما متشافش",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "فشل تحديث حالة المشاهدة",
|
||||
"EPISODES_WATCHED": "{{count}} من {{total}} ديال الحلقات متشافين",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "البيانات الوصفية ديال البوابة ما متوفراش",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "حول",
|
||||
"SEASONS_AND_EPISODES": "المواسم والحلقات",
|
||||
"SEASON_TAB": "الموسم {{number}}",
|
||||
"EPISODE_COUNT_ONE": "حلقة وحدة",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} حلقات",
|
||||
"BACK_TO_PLAYING_EPISODE": "رجوع للحلقة اللي كتشغل",
|
||||
"GRID_VIEW": "عرض الشبكة",
|
||||
"LIST_VIEW": "عرض اللائحة",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "هاد المحتوى ما متوفرش على هاد السيرفر",
|
||||
"PLAYBACK_ERROR": "فشل جلب رابط التشغيل",
|
||||
"LOADING_SEASONS": "جاري تشريج المواسم...",
|
||||
"LOADING_EPISODES": "جاري تشريج الحلقات...",
|
||||
"EPISODES_LOAD_FAILED": "ما قدرناش نشرجيو الحلقات.",
|
||||
"LOADING_ALL_CHANNELS": "كيتم تحميل لائحة القنوات كاملة…",
|
||||
"REFRESH_CHANNEL_LIST": "تحديث لائحة القنوات",
|
||||
"NO_CHANNELS_IN_CATEGORY": "ماكاينش قنوات فهاد الصنف",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "حل المتشافين مؤخراً",
|
||||
"SUBMIT_SHELL_SEARCH": "رسل بحث مساحة العمل",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "بدل ملء الشاشة ديال نافذة التطبيق",
|
||||
"ZOOM_IN_OUT": "كبر ولا صغر التطبيق",
|
||||
"RESET_ZOOM": "رجع تكبير التطبيق للأصل",
|
||||
"TOGGLE_SIDEBAR": "بدل عرض الشريط الجانبي",
|
||||
"M3U_CHANNEL_NUMBER": "اختار قناة M3U بالرقم",
|
||||
"PLAY_PAUSE": "شغل ولا وقف مؤقتاً تشغيل MPV المدمج",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "Абнавіць праграму каналаў (EPG)",
|
||||
"REFRESH_ALL_EPG": "Абнавіць усё",
|
||||
"REMOVE_EPG_SOURCE": "Выдаліць крыніцу EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Актуальна",
|
||||
"EPG_STATUS_STALE": "Патрабуе абнаўлення",
|
||||
"EPG_STATUS_ERROR": "Памылка",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Не ўдалося ачысціць дадзеныя EPG. Калі ласка, паспрабуйце яшчэ раз.",
|
||||
"CLEARING_EPG_DATA": "Ачышчаецца...",
|
||||
"EPG_URL_ERROR": "Уведзенае значэнне не з'яўляецца URL-адрасам.",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Выбраць стандартны відэапрайгравальнік",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Адзіныя элементы кіравання для вэб-прайгравальнікаў",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Выкарыстоўваць агульныя элементы кіравання IPTVnator у HTML5, Video.js і ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Запаўніць прастор вакол плэйра размытай, затемнёнай вокладкай замяст чорных палос.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Панэль наступных эпізодаў",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "На шырокіх вокнах замацоўваць прайгравальнік серыяла злева і паказваць наступныя эпізоды побач з ім.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Спіс каналаў у поўнаэкранным рэжыме",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Падвядзіце мыш да левага краю або націсніце C у поўнаэкранным рэжыме, каб высунуць спіс каналаў і пераключыць канал, не выходзячы з поўнаэкраннага рэжыму.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Спіс каналаў і серый у поўнаэкранным рэжыме",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Навядзіце мыш на левы край, націсніце на яго або націсніце C у поўнаэкранным рэжыме, каб паказаць спіс каналаў прамога эфіру або спіс серый серыяла і пераключыцца, не выходзячы з поўнаэкраннага рэжыму.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Выберыце фармат патоку па змаўчанні (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Выбраць мову праграмы",
|
||||
"THEME_DESCRIPTION": "Выбраць візуальную тэму афармлення",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Малы",
|
||||
"COVER_SIZE_MEDIUM": "Сярэдні",
|
||||
"COVER_SIZE_LARGE": "Вялікі",
|
||||
"COVER_TITLES": "Паказаць назвы пад вокладкамі",
|
||||
"COVER_TITLES_DESCRIPTION": "Выключыце, каб атрымаць сцяну толькі з постараў у сетках фільмаў і серыялаў: назва з'яўляецца на вокладцы пры навядзенні курсора або пераводзе фокусу на яе. Жывыя каналы, вынікі пошуку і стужкі панэлі заўсёды паказваюць свае назвы.",
|
||||
"EPG_VIEW_MODE": "Выгляд праграмы перадач",
|
||||
"EPG_OFFSET_TITLE": "Зрушэнне часу праграмы",
|
||||
"EPG_OFFSET_HINT": "Змяняйце час паказу праграм без абнаўлення даведніка. Выкарыстоўвайце хвіліны ад -720 да 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Перазапусціць праграму",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Адкрыць рэліз GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Што новага",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Заўвагі да версіі {{version}} на GitHub не знойдзены. Рэліз мог быць выдалены або яшчэ не апублікаваны.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Не ўдалося загрузіць заўвагі да версіі.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Адкрыць старонку рэлізаў",
|
||||
"APP_UPDATE_CHANNEL": "Канал абнаўленняў",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable ідзе за выпушчанымі рэлізамі. Nightly ідзе за кожным зліццём у master і аўтаматычна ўсталёўвае папярэднія зборкі. Ужываецца пасля захавання.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Захаваць і праверыць абнаўленні {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Гэты вынік датычыцца канала {{current}}. Захавайце, каб пераключыцца на {{next}} і праверыць яшчэ раз.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Гэта абнаўленне знойдзена на канале {{current}}. Канал {{next}} яшчэ не правераны.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly-зборкі — неправераныя здымкі master. Яны могуць працаваць нестабільна, а іх змены базы даных незваротныя: пасля вяртання на Stable гэтая зборка застанецца ўсталяванай, пакуль не выйдзе больш новы стабільны рэліз. Спачатку зрабіце рэзервовую копію плэйлістоў.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Вы выкарыстоўваеце nightly-зборку. Канал Stable прапануе наступны стабільны рэліз, як толькі ён стане навейшым за гэтую зборку.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: рухавік frame-copy (эксперыментальна)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Відэа малюецца на канвасе праграмы, таму меню і дыялогі паказваюцца па-над ім.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Пачне дзейнічаць пасля перазапуску праграмы.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Выйсці з поўнаэкраннага рэжыму",
|
||||
"CHANNEL_LIST": "Спіс каналаў",
|
||||
"HIDE_CHANNEL_LIST": "Схаваць спіс каналаў",
|
||||
"EPISODE_LIST": "Спіс серый",
|
||||
"HIDE_EPISODE_LIST": "Схаваць спіс серый",
|
||||
"SEARCH_CHANNELS": "Пошук каналаў",
|
||||
"SEARCH_IN": "Пошук у {{title}}",
|
||||
"CLEAR_SEARCH": "Ачысціць пошук",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "перадач",
|
||||
"LOADING": "Загрузка",
|
||||
"QUEUED": "У чарзе",
|
||||
"IMPORT_PANEL_MINIMIZE": "Згарнуць панэль імпарту EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Разгарнуць панэль імпарту EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "Загружаецца: {{count}}",
|
||||
"IMPORT_SUMMARY_QUEUED": "у чарзе: {{count}}",
|
||||
"TOTAL_CHANNELS": "Усяго каналаў",
|
||||
"TOTAL_PROGRAMS": "Усяго перадач",
|
||||
"UP_TO_DATE": "EPG актуальны, сінхранізацыя не патрэбна",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Серыял адзначаны як непрагледжаны",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Не ўдалося абнавіць серыял",
|
||||
"SERIES_WATCH_MENU": "Дзеянні прагляду серыяла",
|
||||
"MOVIE_MARKED_WATCHED": "Адзначана як прагледжанае",
|
||||
"MOVIE_MARKED_UNWATCHED": "Адзначана як непрагледжанае",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Не ўдалося абнавіць стан прагляду",
|
||||
"EPISODES_WATCHED": "{{count}} з {{total}} эпізодаў прагледжана",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Метаданыя партала недаступныя",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Пра фільм",
|
||||
"SEASONS_AND_EPISODES": "Сезоны і эпізоды",
|
||||
"SEASON_TAB": "Сезон {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 эпізод",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} эпізодаў",
|
||||
"BACK_TO_PLAYING_EPISODE": "Назад да бягучага эпізоду",
|
||||
"GRID_VIEW": "Выгляд сеткай",
|
||||
"LIST_VIEW": "Выгляд спісам",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Гэты кантэнт недаступны на дадзеным серверы",
|
||||
"PLAYBACK_ERROR": "Не ўдалося атрымаць URL для прайгравання",
|
||||
"LOADING_SEASONS": "Загрузка сезонаў...",
|
||||
"LOADING_EPISODES": "Загрузка серый...",
|
||||
"EPISODES_LOAD_FAILED": "Не ўдалося загрузіць серыі.",
|
||||
"LOADING_ALL_CHANNELS": "Загрузка поўнага спісу каналаў…",
|
||||
"REFRESH_CHANNEL_LIST": "Абнавіць спіс каналаў",
|
||||
"NO_CHANNELS_IN_CATEGORY": "У гэтай катэгорыі няма каналаў",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Адкрыць нядаўна прагледжанае",
|
||||
"SUBMIT_SHELL_SEARCH": "Выканаць пошук у працоўнай вобласці",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Пераключыць поўнаэкранны рэжым акна праграмы",
|
||||
"ZOOM_IN_OUT": "Павялічыць або паменшыць маштаб праграмы",
|
||||
"RESET_ZOOM": "Скінуць маштаб праграмы",
|
||||
"TOGGLE_SIDEBAR": "Пераключыць бакавую панэль",
|
||||
"M3U_CHANNEL_NUMBER": "Выбраць M3U-канал па нумары",
|
||||
"PLAY_PAUSE": "Прайграць або прыпыніць Embedded MPV",
|
||||
|
||||
@@ -268,10 +268,10 @@
|
||||
"GENERAL_SUBTITLE": "Allgemeine App-Einstellungen ändern",
|
||||
"PLAYBACK": "Wiedergabe",
|
||||
"EPG_SOURCES": "EPG-Quellen",
|
||||
"EPG_SOURCES_DESCRIPTION": "Eine oder mehrere URLs als EPG-Quellen hinzufügen",
|
||||
"EPG_SOURCES_DESCRIPTION": "Eine oder mehrere XMLTV-Quellen hinzufügen: Links oder lokale Dateien",
|
||||
"PLAYLIST_EPG_SOURCES_AUTO_SELECTED": "{{enabled}} von {{detected}} erkannten EPG-Quellen der Wiedergabeliste wurden automatisch für den Import ausgewählt.",
|
||||
"EPG_EMPTY_TITLE": "Noch keine EPG-Quellen",
|
||||
"EPG_EMPTY_HINT": "Füge eine URL hinzu, um Programmdaten für deine Kanäle abzurufen.",
|
||||
"EPG_EMPTY_HINT": "Füge eine URL oder eine lokale XMLTV-Datei hinzu, um Programmdaten für deine Kanäle zu laden.",
|
||||
"PREFER_UPLOADED_EPG_TITLE": "Eigene EPG-Quellen bei Xtream-Playlists bevorzugen",
|
||||
"PREFER_UPLOADED_EPG_HINT": "Standardmäßig nutzen Xtream-Playlists die Programmdaten deines Anbieters, und dein hochgeladenes EPG füllt nur fehlende Kanäle. Aktiviere die Option, um stattdessen zuerst dein hochgeladenes EPG zu nutzen.",
|
||||
"DATA": "Daten",
|
||||
@@ -314,11 +314,12 @@
|
||||
"HERO_DESCRIPTION": "Passen Sie Wiedergabe, Programmführer, Fernzugriff und Datenverwaltung zentral an, ohne den Stil des Arbeitsbereichs zu verlieren.",
|
||||
"SUBTITLE": "Die App-Einstellungen ändern",
|
||||
"DESCRIPTION": "Bitte vergessen Sie nicht die Änderungen mit dem Speicher-Button zu bestätigen",
|
||||
"EPG_URL_LABEL": "TV-Programm (EPG) URL",
|
||||
"EPG_URL_PLACEHOLDER": "URL eingeben",
|
||||
"EPG_URL_LABEL": "EPG-Quelle (URL oder lokale Datei, *.xml oder *.xml.gz)",
|
||||
"EPG_URL_PLACEHOLDER": "URL oder Dateipfad eingeben",
|
||||
"REFRESH_EPG": "TV-Programm aktualisieren",
|
||||
"REFRESH_ALL_EPG": "Alle aktualisieren",
|
||||
"REMOVE_EPG_SOURCE": "EPG-Quelle entfernen",
|
||||
"BROWSE_EPG_FILE": "Lokale XMLTV-Datei auswählen…",
|
||||
"EPG_STATUS_FRESH": "Aktuell",
|
||||
"EPG_STATUS_STALE": "Aktualisierung nötig",
|
||||
"EPG_STATUS_ERROR": "Fehlgeschlagen",
|
||||
@@ -363,7 +364,10 @@
|
||||
"EPG_DATA_CLEARED": "EPG-Daten wurden gelöscht",
|
||||
"EPG_DATA_CLEAR_FAILED": "EPG-Daten konnten nicht gelöscht werden. Bitte erneut versuchen.",
|
||||
"CLEARING_EPG_DATA": "Wird gelöscht…",
|
||||
"EPG_URL_ERROR": "Der Wert ist keine gültige URL",
|
||||
"EPG_URL_ERROR": "Gib einen http(s)-Link, einen absoluten Dateipfad oder eine file://-URL ein",
|
||||
"EPG_SOURCE_LIST_TITLE": "Quellenliste",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Unterstützt werden XMLTV-Links (http/https) und Dateien auf diesem Computer, als einfaches .xml oder gzip-komprimiertes .xml.gz. Lokale Dateien brauchen einen absoluten Pfad oder eine file://-URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Beispiele:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Wählen Sie den Standard-Videoplayer aus",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Einheitliche Steuerung für Web-Player",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Verwendet die gemeinsamen IPTVnator-Steuerelemente in HTML5, Video.js und ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Füllt den Bereich um den Player mit einem unscharfen, gedimmten Poster auf, anstelle von einfachen schwarzen Balken.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Folgenleiste „Als Nächstes“",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Dockt den Serienplayer in breiten Fenstern links an und zeigt daneben die kommenden Folgen an.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Senderliste im Vollbild",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Bewege die Maus im Vollbild an den linken Rand oder drücke C, um die Senderliste einzublenden und den Sender zu wechseln, ohne das Vollbild zu verlassen.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Sender- und Episodenliste im Vollbild",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Bewege die Maus im Vollbild an den linken Rand, klicke darauf oder drücke C, um die Senderliste eines Live-Streams oder die Episodenliste einer Serie einzublenden und zu wechseln, ohne das Vollbild zu verlassen.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Auto bevorzugt HLS. Schlägt Live-TV in MPV, VLC oder Embedded MPV fehl, den Player stoppen, ts wählen, speichern und den Sender erneut öffnen. Webplayer können bei einem anfänglichen HLS-HTTP-Fehler einmal angebotenes TS versuchen.",
|
||||
"LANGUAGE_DESCRIPTION": "Wählen Sie die Sprache der Anwendung aus",
|
||||
"THEME_DESCRIPTION": "Wählen Sie das visuelle Thema der Anwendung aus",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Klein",
|
||||
"COVER_SIZE_MEDIUM": "Mittel",
|
||||
"COVER_SIZE_LARGE": "Groß",
|
||||
"COVER_TITLES": "Titel unter Covern anzeigen",
|
||||
"COVER_TITLES_DESCRIPTION": "Deaktivieren Sie diese Option für eine reine Poster-Wand in Film- und Serienrastern: Der Titel erscheint auf dem Cover, wenn Sie mit der Maus darauf zeigen oder es per Tastatur fokussieren. Live-Kanäle, Suchergebnisse und Dashboard-Reihen behalten ihre Beschriftungen.",
|
||||
"EPG_VIEW_MODE": "Programmführer-Ansicht",
|
||||
"EPG_OFFSET_TITLE": "EPG-Zeitverschiebung",
|
||||
"EPG_OFFSET_HINT": "Angezeigte Sendezeiten verschieben, ohne den Programmführer zu aktualisieren. Minuten von -720 bis 720 verwenden.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Anwendung neu starten",
|
||||
"APP_UPDATE_OPEN_RELEASE": "GitHub-Release öffnen",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Was ist neu",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Für {{version}} wurden auf GitHub keine Release-Notes gefunden. Das Release wurde möglicherweise entfernt oder ist noch nicht veröffentlicht.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Die Release-Notes konnten nicht geladen werden.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Release-Seite öffnen",
|
||||
"APP_UPDATE_CHANNEL": "Update-Kanal",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable folgt den veröffentlichten Releases. Nightly folgt jedem Merge in master und installiert Vorabversionen automatisch. Gilt nach dem Speichern.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Speichern und auf {{channel}}-Updates prüfen",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Dieses Ergebnis gilt für den Kanal {{current}}. Speichern, um zu {{next}} zu wechseln und erneut zu prüfen.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Dieses Update wurde im Kanal {{current}} gefunden. Der Kanal {{next}} wurde noch nicht geprüft.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly-Builds sind ungetestete Momentaufnahmen von master. Sie können fehlerhaft sein, und ihre Datenbankänderungen sind dauerhaft: Nach dem Wechsel zurück zu Stable bleibt dieser Build installiert, bis ein neueres stabiles Release erscheint. Sichern Sie zuerst Ihre Playlists.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Sie verwenden einen Nightly-Build. Der Stable-Kanal bietet das nächste stabile Release an, sobald es neuer als dieser Build ist.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: Frame-Copy-Engine (experimentell)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Das Video wird auf die App-Leinwand gerendert, sodass Menüs und Dialoge darüber liegen.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Wird nach einem Neustart der App wirksam.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Vollbild beenden",
|
||||
"CHANNEL_LIST": "Senderliste",
|
||||
"HIDE_CHANNEL_LIST": "Senderliste ausblenden",
|
||||
"EPISODE_LIST": "Episodenliste",
|
||||
"HIDE_EPISODE_LIST": "Episodenliste ausblenden",
|
||||
"SEARCH_CHANNELS": "Sender suchen",
|
||||
"SEARCH_IN": "In {{title}} suchen",
|
||||
"CLEAR_SEARCH": "Suche löschen",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "Programme",
|
||||
"LOADING": "Wird geladen",
|
||||
"QUEUED": "In Warteschlange",
|
||||
"IMPORT_PANEL_MINIMIZE": "EPG-Importpanel minimieren",
|
||||
"IMPORT_PANEL_EXPAND": "EPG-Importpanel ausklappen",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} wird geladen",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} in Warteschlange",
|
||||
"TOTAL_CHANNELS": "Sender insgesamt",
|
||||
"TOTAL_PROGRAMS": "Programme insgesamt",
|
||||
"UP_TO_DATE": "EPG ist aktuell, keine Synchronisation nötig",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Serie als nicht angesehen markiert",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Aktualisierung der Serie fehlgeschlagen",
|
||||
"SERIES_WATCH_MENU": "Aktionen zum Markieren der Serie",
|
||||
"MOVIE_MARKED_WATCHED": "Als angesehen markiert",
|
||||
"MOVIE_MARKED_UNWATCHED": "Als nicht angesehen markiert",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Aktualisierung des Status „angesehen“ fehlgeschlagen",
|
||||
"EPISODES_WATCHED": "{{count}} von {{total}} Folgen angesehen",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Portal-Metadaten fehlen",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Info",
|
||||
"SEASONS_AND_EPISODES": "Staffeln und Folgen",
|
||||
"SEASON_TAB": "Staffel {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 Episode",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} Episoden",
|
||||
"BACK_TO_PLAYING_EPISODE": "Zurück zur laufenden Folge",
|
||||
"GRID_VIEW": "Rasteransicht",
|
||||
"LIST_VIEW": "Listenansicht",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Dieser Inhalt ist auf diesem Server nicht verfügbar",
|
||||
"PLAYBACK_ERROR": "Wiedergabe-URL konnte nicht abgerufen werden",
|
||||
"LOADING_SEASONS": "Staffeln werden geladen …",
|
||||
"LOADING_EPISODES": "Episoden werden geladen …",
|
||||
"EPISODES_LOAD_FAILED": "Die Episoden konnten nicht geladen werden.",
|
||||
"LOADING_ALL_CHANNELS": "Vollständige Senderliste wird geladen…",
|
||||
"REFRESH_CHANNEL_LIST": "Senderliste aktualisieren",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Keine Sender in dieser Kategorie",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Kürzlich angesehene Elemente öffnen",
|
||||
"SUBMIT_SHELL_SEARCH": "Arbeitsbereichssuche absenden",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Vollbild für das App-Fenster umschalten",
|
||||
"ZOOM_IN_OUT": "App vergrößern oder verkleinern",
|
||||
"RESET_ZOOM": "App-Zoom zurücksetzen",
|
||||
"TOGGLE_SIDEBAR": "Seitenleiste umschalten",
|
||||
"M3U_CHANNEL_NUMBER": "M3U-Kanal per Nummer auswählen",
|
||||
"PLAY_PAUSE": "Eingebettete MPV-Wiedergabe starten oder pausieren",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "Ανανέωση EPG",
|
||||
"REFRESH_ALL_EPG": "Ανανέωση όλων",
|
||||
"REMOVE_EPG_SOURCE": "Αφαίρεση πηγής EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Ενημερωμένο",
|
||||
"EPG_STATUS_STALE": "Χρειάζεται ανανέωση",
|
||||
"EPG_STATUS_ERROR": "Αποτυχία",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Αποτυχία διαγραφής δεδομένων EPG. Παρακαλώ δοκιμάστε ξανά.",
|
||||
"CLEARING_EPG_DATA": "Διαγραφή…",
|
||||
"EPG_URL_ERROR": "Η τιμή δεν είναι έγκυρη διεύθυνση URL",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Επιλογή προεπιλεγμένου προγράμματος αναπαραγωγής βίντεο",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Ενοποιημένα στοιχεία ελέγχου για web players",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Χρησιμοποιήστε τα κοινά στοιχεία ελέγχου του IPTVnator σε HTML5, Video.js και ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Γεμίστε το χώρο γύρω από τον αναπαραγωγό με θολωμένη, σκοτεινή αφίσα αντί για απλές μαύρες ράβδους.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Λίστα επόμενων επεισοδίων",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Σε πλατιά παράθυρα, ο αναπαραγωγός σειρών αγκυρώνεται στα αριστερά και τα επόμενα επεισόδια εμφανίζονται δίπλα του.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Λίστα καναλιών σε πλήρη οθόνη",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Σε πλήρη οθόνη, μετακινήστε το ποντίκι στην αριστερή άκρη ή πατήστε C για να εμφανιστεί η λίστα καναλιών και να αλλάξετε κανάλι χωρίς να βγείτε από την πλήρη οθόνη.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Λίστα καναλιών και επεισοδίων σε πλήρη οθόνη",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Αφήστε το ποντίκι στην αριστερή άκρη, κάντε κλικ σε αυτήν ή πατήστε C σε πλήρη οθόνη για να εμφανίσετε τη λίστα καναλιών μιας ζωντανής ροής ή τη λίστα επεισοδίων μιας σειράς και να αλλάξετε χωρίς να βγείτε από την πλήρη οθόνη.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Επιλογή προεπιλεγμένης μορφής ροής (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Επιλέξτε τη γλώσσα της εφαρμογής",
|
||||
"THEME_DESCRIPTION": "Επιλέξτε οπτικό θέμα της εφαρμογής",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Μικρό",
|
||||
"COVER_SIZE_MEDIUM": "Μεσαίο",
|
||||
"COVER_SIZE_LARGE": "Μεγάλο",
|
||||
"COVER_TITLES": "Εμφάνιση τίτλων κάτω από τα εξώφυλλα",
|
||||
"COVER_TITLES_DESCRIPTION": "Απενεργοποιήστε το για έναν τοίχο μόνο με αφίσες στα πλέγματα ταινιών και σειρών: ο τίτλος εμφανίζεται πάνω στο εξώφυλλο όταν περάσετε το ποντίκι από πάνω του ή το εστιάσετε. Τα ζωντανά κανάλια, τα αποτελέσματα αναζήτησης και οι σειρές του πίνακα διατηρούν τις ετικέτες τους.",
|
||||
"EPG_VIEW_MODE": "Προβολή οδηγού",
|
||||
"EPG_OFFSET_TITLE": "Μετατόπιση ώρας EPG",
|
||||
"EPG_OFFSET_HINT": "Μετατοπίστε τις εμφανιζόμενες ώρες προγραμμάτων χωρίς ανανέωση του οδηγού. Χρησιμοποιήστε λεπτά από -720 έως 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Επανεκκίνηση εφαρμογής",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Άνοιγμα GitHub release",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Τι νέο υπάρχει",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Δεν βρέθηκαν σημειώσεις έκδοσης για την {{version}} στο GitHub. Η έκδοση μπορεί να αφαιρέθηκε ή να μην έχει δημοσιευτεί ακόμη.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Δεν ήταν δυνατή η φόρτωση των σημειώσεων έκδοσης.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Άνοιγμα σελίδας εκδόσεων",
|
||||
"APP_UPDATE_CHANNEL": "Κανάλι ενημερώσεων",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Το Stable ακολουθεί τις δημοσιευμένες εκδόσεις. Το Nightly ακολουθεί κάθε συγχώνευση στο master και εγκαθιστά αυτόματα προκαταρκτικές εκδόσεις. Εφαρμόζεται κατά την αποθήκευση.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Αποθήκευση και έλεγχος για ενημερώσεις {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Αυτό το αποτέλεσμα αφορά το κανάλι {{current}}. Αποθηκεύστε για να αλλάξετε σε {{next}} και ελέγξτε ξανά.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Αυτή η ενημέρωση βρέθηκε στο κανάλι {{current}}. Το κανάλι {{next}} δεν έχει ελεγχθεί ακόμη.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Οι εκδόσεις nightly είναι μη δοκιμασμένα στιγμιότυπα του master. Μπορεί να παρουσιάζουν σφάλματα και οι αλλαγές τους στη βάση δεδομένων είναι μόνιμες: αν επιστρέψετε στο Stable, αυτή η έκδοση παραμένει εγκατεστημένη μέχρι να υπάρξει νεότερη σταθερή έκδοση. Δημιουργήστε πρώτα αντίγραφο ασφαλείας των λιστών αναπαραγωγής.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Χρησιμοποιείτε έκδοση nightly. Το κανάλι Stable θα προσφέρει την επόμενη σταθερή έκδοση μόλις είναι νεότερη από αυτήν.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: μηχανή frame-copy (πειραματικό)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Το βίντεο σχεδιάζεται στον καμβά της εφαρμογής, ώστε τα μενού και τα παράθυρα διαλόγου να εμφανίζονται από πάνω του.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Ισχύει μετά την επανεκκίνηση της εφαρμογής.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Έξοδος από πλήρη οθόνη",
|
||||
"CHANNEL_LIST": "Λίστα καναλιών",
|
||||
"HIDE_CHANNEL_LIST": "Απόκρυψη λίστας καναλιών",
|
||||
"EPISODE_LIST": "Λίστα επεισοδίων",
|
||||
"HIDE_EPISODE_LIST": "Απόκρυψη λίστας επεισοδίων",
|
||||
"SEARCH_CHANNELS": "Αναζήτηση καναλιών",
|
||||
"SEARCH_IN": "Αναζήτηση σε {{title}}",
|
||||
"CLEAR_SEARCH": "Καθαρισμός αναζήτησης",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "προγράμματα",
|
||||
"LOADING": "Φόρτωση",
|
||||
"QUEUED": "Σε ουρά",
|
||||
"IMPORT_PANEL_MINIMIZE": "Ελαχιστοποίηση πίνακα εισαγωγής EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Ανάπτυξη πίνακα εισαγωγής EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} σε φόρτωση",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} σε αναμονή",
|
||||
"TOTAL_CHANNELS": "Σύνολο καναλιών",
|
||||
"TOTAL_PROGRAMS": "Σύνολο προγραμμάτων",
|
||||
"UP_TO_DATE": "Το EPG είναι ενημερωμένο, δεν απαιτείται συγχρονισμός",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Η σειρά σημειώθηκε ως μη προβληθείσα",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Η ενημέρωση της σειράς απέτυχε",
|
||||
"SERIES_WATCH_MENU": "Ενέργειες παρακολούθησης σειράς",
|
||||
"MOVIE_MARKED_WATCHED": "Σημειώθηκε ως προβληθέν",
|
||||
"MOVIE_MARKED_UNWATCHED": "Σημειώθηκε ως μη προβληθέν",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Η ενημέρωση της κατάστασης προβολής απέτυχε",
|
||||
"EPISODES_WATCHED": "{{count}} από {{total}} επεισόδια προβλήθηκαν",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Τα μεταδεδομένα της πύλης δεν είναι διαθέσιμα",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Σχετικά",
|
||||
"SEASONS_AND_EPISODES": "Σεζόν και επεισόδια",
|
||||
"SEASON_TAB": "Σεζόν {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 επεισόδιο",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} επεισόδια",
|
||||
"BACK_TO_PLAYING_EPISODE": "Πίσω στο επεισόδιο που παίζει",
|
||||
"GRID_VIEW": "Προβολή πλέγματος",
|
||||
"LIST_VIEW": "Προβολή λίστας",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Αυτό το περιεχόμενο δεν είναι διαθέσιμο σε αυτόν τον διακομιστή",
|
||||
"PLAYBACK_ERROR": "Αποτυχία λήψης διεύθυνσης URL αναπαραγωγής",
|
||||
"LOADING_SEASONS": "Φόρτωση σεζόν...",
|
||||
"LOADING_EPISODES": "Φόρτωση επεισοδίων...",
|
||||
"EPISODES_LOAD_FAILED": "Δεν ήταν δυνατή η φόρτωση των επεισοδίων.",
|
||||
"LOADING_ALL_CHANNELS": "Φόρτωση πλήρους λίστας καναλιών…",
|
||||
"REFRESH_CHANNEL_LIST": "Ανανέωση λίστας καναλιών",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Δεν υπάρχουν κανάλια σε αυτήν την κατηγορία",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Άνοιγμα πρόσφατων προβολών",
|
||||
"SUBMIT_SHELL_SEARCH": "Υποβολή αναζήτησης χώρου εργασίας",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Εναλλαγή πλήρους οθόνης παραθύρου εφαρμογής",
|
||||
"ZOOM_IN_OUT": "Μεγέθυνση ή σμίκρυνση της εφαρμογής",
|
||||
"RESET_ZOOM": "Επαναφορά ζουμ εφαρμογής",
|
||||
"TOGGLE_SIDEBAR": "Εναλλαγή πλαϊνής μπάρας",
|
||||
"M3U_CHANNEL_NUMBER": "Επιλογή καναλιού M3U με αριθμό",
|
||||
"PLAY_PAUSE": "Αναπαραγωγή ή παύση ενσωματωμένου MPV",
|
||||
|
||||
@@ -268,10 +268,10 @@
|
||||
"GENERAL_SUBTITLE": "Change the configuration of the application",
|
||||
"PLAYBACK": "Playback",
|
||||
"EPG_SOURCES": "EPG sources",
|
||||
"EPG_SOURCES_DESCRIPTION": "Add one or more URLs as EPG sources",
|
||||
"EPG_SOURCES_DESCRIPTION": "Add one or more XMLTV sources: remote links or local files",
|
||||
"PLAYLIST_EPG_SOURCES_AUTO_SELECTED": "{{enabled}} of {{detected}} detected playlist EPG sources were auto-selected for import.",
|
||||
"EPG_EMPTY_TITLE": "No EPG sources yet",
|
||||
"EPG_EMPTY_HINT": "Add a URL to fetch program data for your channels.",
|
||||
"EPG_EMPTY_HINT": "Add a URL or a local XMLTV file to fetch program data for your channels.",
|
||||
"PREFER_UPLOADED_EPG_TITLE": "Prefer your EPG sources for Xtream playlists",
|
||||
"PREFER_UPLOADED_EPG_HINT": "By default, Xtream playlists use the program guide from your provider, and your uploaded EPG only fills missing channels. Turn this on to use your uploaded EPG first instead.",
|
||||
"DATA": "Data",
|
||||
@@ -314,11 +314,12 @@
|
||||
"HERO_DESCRIPTION": "Tune playback, guides, remote access and data handling from one place without losing the current workspace style.",
|
||||
"SUBTITLE": "Change the configuration of the application",
|
||||
"DESCRIPTION": "Please adapt the options from the list below and confirm with the save changes button",
|
||||
"EPG_URL_LABEL": "EPG Url (*.xml or *.xml.gz)",
|
||||
"EPG_URL_PLACEHOLDER": "Enter an URL",
|
||||
"EPG_URL_LABEL": "EPG source (URL or local file, *.xml or *.xml.gz)",
|
||||
"EPG_URL_PLACEHOLDER": "Enter a URL or a local file path",
|
||||
"REFRESH_EPG": "Refresh EPG",
|
||||
"REFRESH_ALL_EPG": "Refresh all",
|
||||
"REMOVE_EPG_SOURCE": "Remove EPG source",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Up to date",
|
||||
"EPG_STATUS_STALE": "Needs refresh",
|
||||
"EPG_STATUS_ERROR": "Failed",
|
||||
@@ -363,7 +364,10 @@
|
||||
"EPG_DATA_CLEARED": "EPG data has been cleared",
|
||||
"EPG_DATA_CLEAR_FAILED": "Failed to clear EPG data. Please try again.",
|
||||
"CLEARING_EPG_DATA": "Clearing…",
|
||||
"EPG_URL_ERROR": "The value is not a valid URL",
|
||||
"EPG_URL_ERROR": "Enter an http(s) link, an absolute file path or a file:// URL",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Select default video player",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Unified controls for web players",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Use IPTVnator's shared controls in HTML5, Video.js, and ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Fill the space around the player with a blurred, dimmed poster instead of plain black bars.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Up Next episode rail",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "On wide windows, dock the series player to the left and show the upcoming episodes beside it.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Channel list in fullscreen",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Rest the mouse on the left edge or press C while in fullscreen to slide in the channel list and switch channels without leaving fullscreen.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Channel and episode list in fullscreen",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Rest the mouse on the left edge, click it, or press C while in fullscreen to slide in the channel list of a live stream or the episode list of a series and switch without leaving fullscreen.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Auto prefers HLS. If live playback fails in MPV, VLC or Embedded MPV, stop the player, select ts, save and reopen the channel. Web players can try advertised TS once after an initial HLS HTTP failure.",
|
||||
"LANGUAGE_DESCRIPTION": "Select language of the application",
|
||||
"THEME_DESCRIPTION": "Select visual theme of the application",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Small",
|
||||
"COVER_SIZE_MEDIUM": "Medium",
|
||||
"COVER_SIZE_LARGE": "Large",
|
||||
"COVER_TITLES": "Show titles under covers",
|
||||
"COVER_TITLES_DESCRIPTION": "Turn off for a posters-only wall in movie and series grids: the title appears on the cover when you point at it or focus it. Live channels, search results and dashboard rails keep their labels.",
|
||||
"EPG_VIEW_MODE": "Guide view",
|
||||
"EPG_OFFSET_TITLE": "EPG time offset",
|
||||
"EPG_OFFSET_HINT": "Shift displayed programme times without refreshing the guide. Use minutes, from -720 to 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Restart application",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
|
||||
"APP_UPDATE_RELEASE_NOTES": "What's new",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "No release notes were found for {{version}} on GitHub. The release may have been removed or is not published yet.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Release notes could not be loaded.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Open releases page",
|
||||
"APP_UPDATE_CHANNEL": "Update channel",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable follows the tagged releases. Nightly follows every merge into master and installs prerelease builds automatically. Applies on Save.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Save and check for {{channel}} updates",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "This result is for the {{current}} channel. Save to switch to {{next}} and check again.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "This update was found on the {{current}} channel. The {{next}} channel has not been checked yet.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly builds are untested snapshots of master. They may break, and their database changes are permanent: switching back to Stable keeps this build until the next stable release is newer. Back up your playlists first.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "You are running a nightly build. The Stable channel will offer the next stable release once it is newer than this build.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: frame-copy engine (experimental)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Render video onto the app canvas so menus and dialogs stack above it.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Takes effect after restarting the app.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Exit fullscreen",
|
||||
"CHANNEL_LIST": "Channel list",
|
||||
"HIDE_CHANNEL_LIST": "Hide channel list",
|
||||
"EPISODE_LIST": "Episode list",
|
||||
"HIDE_EPISODE_LIST": "Hide episode list",
|
||||
"SEARCH_CHANNELS": "Search channels",
|
||||
"SEARCH_IN": "Search in {{title}}",
|
||||
"CLEAR_SEARCH": "Clear search",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programs",
|
||||
"LOADING": "Loading",
|
||||
"QUEUED": "Queued",
|
||||
"IMPORT_PANEL_MINIMIZE": "Minimize EPG import panel",
|
||||
"IMPORT_PANEL_EXPAND": "Expand EPG import panel",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} loading",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} queued",
|
||||
"TOTAL_CHANNELS": "Total channels",
|
||||
"TOTAL_PROGRAMS": "Total programs",
|
||||
"UP_TO_DATE": "EPG is up-to-date, no sync needed",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Series marked as unwatched",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Updating the series failed",
|
||||
"SERIES_WATCH_MENU": "Series watch actions",
|
||||
"MOVIE_MARKED_WATCHED": "Marked as watched",
|
||||
"MOVIE_MARKED_UNWATCHED": "Marked as unwatched",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Updating the watched state failed",
|
||||
"EPISODES_WATCHED": "{{count}} of {{total}} episodes watched",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Portal metadata unavailable",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "About",
|
||||
"SEASONS_AND_EPISODES": "Seasons and Episodes",
|
||||
"SEASON_TAB": "Season {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 episode",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} episodes",
|
||||
"BACK_TO_PLAYING_EPISODE": "Back to playing episode",
|
||||
"GRID_VIEW": "Grid view",
|
||||
"LIST_VIEW": "List view",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "This content is not available on this server",
|
||||
"PLAYBACK_ERROR": "Failed to get playback URL",
|
||||
"LOADING_SEASONS": "Loading seasons...",
|
||||
"LOADING_EPISODES": "Loading episodes...",
|
||||
"EPISODES_LOAD_FAILED": "The episodes could not be loaded.",
|
||||
"LOADING_ALL_CHANNELS": "Loading full channel list…",
|
||||
"REFRESH_CHANNEL_LIST": "Refresh channel list",
|
||||
"NO_CHANNELS_IN_CATEGORY": "No channels in this category",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Open recently viewed",
|
||||
"SUBMIT_SHELL_SEARCH": "Submit workspace search",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Toggle app window fullscreen",
|
||||
"ZOOM_IN_OUT": "Zoom the app in or out",
|
||||
"RESET_ZOOM": "Reset app zoom",
|
||||
"TOGGLE_SIDEBAR": "Toggle sidebar",
|
||||
"M3U_CHANNEL_NUMBER": "Select M3U channel by number",
|
||||
"PLAY_PAUSE": "Play or pause embedded MPV playback",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "Actualizar EPG",
|
||||
"REFRESH_ALL_EPG": "Actualizar todo",
|
||||
"REMOVE_EPG_SOURCE": "Eliminar fuente EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Actualizado",
|
||||
"EPG_STATUS_STALE": "Necesita actualización",
|
||||
"EPG_STATUS_ERROR": "Error",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "No se pudieron borrar los datos EPG. Inténtalo de nuevo.",
|
||||
"CLEARING_EPG_DATA": "Borrando…",
|
||||
"EPG_URL_ERROR": "El valor no es una URL válida.",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Seleccionar reproductor de vídeo predeterminado",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Controles unificados para reproductores web",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Usa los controles compartidos de IPTVnator en HTML5, Video.js y ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Rellena el espacio alrededor del reproductor con una imagen de póster desenfocada y oscurecida en lugar de barras negras simples.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Panel de próximos episodios",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "En ventanas anchas, ancla el reproductor de series a la izquierda y muestra los próximos episodios a su lado.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Lista de canales en pantalla completa",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "En pantalla completa, acerca el ratón al borde izquierdo o pulsa C para desplegar la lista de canales y cambiar de canal sin salir de la pantalla completa.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Lista de canales y episodios en pantalla completa",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Deja el ratón en el borde izquierdo, haz clic en él o pulsa C en pantalla completa para mostrar la lista de canales de una emisión en directo o la lista de episodios de una serie y cambiar sin salir de la pantalla completa.",
|
||||
"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.",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Pequeño",
|
||||
"COVER_SIZE_MEDIUM": "Mediano",
|
||||
"COVER_SIZE_LARGE": "Grande",
|
||||
"COVER_TITLES": "Mostrar títulos debajo de las portadas",
|
||||
"COVER_TITLES_DESCRIPTION": "Desactívalo para lograr un muro de solo pósteres en las cuadrículas de películas y series: el título aparece sobre la portada al pasar el cursor por encima o al enfocarla. Los canales en vivo, los resultados de búsqueda y los carriles del panel mantienen sus etiquetas.",
|
||||
"EPG_VIEW_MODE": "Vista de la guía",
|
||||
"EPG_OFFSET_TITLE": "Desfase horario de la EPG",
|
||||
"EPG_OFFSET_HINT": "Desplaza las horas mostradas de los programas sin actualizar la guía. Usa minutos, de -720 a 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Reiniciar aplicación",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Abrir release de GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Novedades",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "No se encontraron notas de la versión {{version}} en GitHub. Es posible que la versión se haya eliminado o aún no esté publicada.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "No se pudieron cargar las notas de la versión.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Abrir página de versiones",
|
||||
"APP_UPDATE_CHANNEL": "Canal de actualizaciones",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable sigue las versiones publicadas. Nightly sigue cada fusión en master e instala compilaciones preliminares automáticamente. Se aplica al guardar.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Guardar y comprobar actualizaciones de {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Este resultado corresponde al canal {{current}}. Guarda para cambiar a {{next}} y comprobar de nuevo.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Esta actualización se encontró en el canal {{current}}. El canal {{next}} aún no se ha comprobado.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Las compilaciones nightly son instantáneas sin probar de master. Pueden fallar y sus cambios en la base de datos son permanentes: al volver a Stable, esta compilación se mantiene instalada hasta que haya una versión estable más reciente. Haz primero una copia de seguridad de tus listas.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Estás usando una compilación nightly. El canal Stable ofrecerá la próxima versión estable en cuanto sea más reciente que esta compilación.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: motor frame-copy (experimental)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "El vídeo se dibuja en el lienzo de la aplicación, de modo que los menús y diálogos quedan encima.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Se aplica tras reiniciar la aplicación.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Salir de pantalla completa",
|
||||
"CHANNEL_LIST": "Lista de canales",
|
||||
"HIDE_CHANNEL_LIST": "Ocultar la lista de canales",
|
||||
"EPISODE_LIST": "Lista de episodios",
|
||||
"HIDE_EPISODE_LIST": "Ocultar la lista de episodios",
|
||||
"SEARCH_CHANNELS": "Buscar canales",
|
||||
"SEARCH_IN": "Buscar en {{title}}",
|
||||
"CLEAR_SEARCH": "Borrar búsqueda",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programas",
|
||||
"LOADING": "Cargando",
|
||||
"QUEUED": "En cola",
|
||||
"IMPORT_PANEL_MINIMIZE": "Minimizar el panel de importación de EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Expandir el panel de importación de EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} cargando",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} en cola",
|
||||
"TOTAL_CHANNELS": "Total de canales",
|
||||
"TOTAL_PROGRAMS": "Total de programas",
|
||||
"UP_TO_DATE": "EPG actualizada, no se necesita sincronización",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Serie marcada como no vista",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "La actualización de la serie falló",
|
||||
"SERIES_WATCH_MENU": "Acciones de visualización de la serie",
|
||||
"MOVIE_MARKED_WATCHED": "Marcado como visto",
|
||||
"MOVIE_MARKED_UNWATCHED": "Marcado como no visto",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "La actualización del estado de visto falló",
|
||||
"EPISODES_WATCHED": "{{count}} de {{total}} episodios vistos",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Metadatos del portal no disponibles",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Acerca de",
|
||||
"SEASONS_AND_EPISODES": "Temporadas y episodios",
|
||||
"SEASON_TAB": "Temporada {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 episodio",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} episodios",
|
||||
"BACK_TO_PLAYING_EPISODE": "Volver al episodio en reproducción",
|
||||
"GRID_VIEW": "Vista de cuadrícula",
|
||||
"LIST_VIEW": "Vista de lista",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Este contenido no está disponible en este servidor",
|
||||
"PLAYBACK_ERROR": "No se pudo obtener la URL de reproducción",
|
||||
"LOADING_SEASONS": "Cargando temporadas…",
|
||||
"LOADING_EPISODES": "Cargando episodios…",
|
||||
"EPISODES_LOAD_FAILED": "No se pudieron cargar los episodios.",
|
||||
"LOADING_ALL_CHANNELS": "Cargando la lista completa de canales…",
|
||||
"REFRESH_CHANNEL_LIST": "Actualizar lista de canales",
|
||||
"NO_CHANNELS_IN_CATEGORY": "No hay canales en esta categoría",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Abrir vistos recientemente",
|
||||
"SUBMIT_SHELL_SEARCH": "Enviar búsqueda del espacio de trabajo",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Alternar pantalla completa de la ventana de la aplicación",
|
||||
"ZOOM_IN_OUT": "Acercar o alejar la aplicación",
|
||||
"RESET_ZOOM": "Restablecer el zoom de la aplicación",
|
||||
"TOGGLE_SIDEBAR": "Mostrar u ocultar barra lateral",
|
||||
"M3U_CHANNEL_NUMBER": "Seleccionar canal M3U por número",
|
||||
"PLAY_PAUSE": "Reproducir o pausar MPV integrado",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "Mise à jour de l'EPG",
|
||||
"REFRESH_ALL_EPG": "Tout actualiser",
|
||||
"REMOVE_EPG_SOURCE": "Supprimer la source EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "À jour",
|
||||
"EPG_STATUS_STALE": "À actualiser",
|
||||
"EPG_STATUS_ERROR": "Échec",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Échec de l'effacement des données EPG. Veuillez réessayer.",
|
||||
"CLEARING_EPG_DATA": "Effacement…",
|
||||
"EPG_URL_ERROR": "La valeur n'est pas une URL valide",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Sélectionnez le lecteur vidéo par défaut",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Commandes unifiées pour les lecteurs web",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Utilise les commandes partagées d’IPTVnator dans HTML5, Video.js et ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Remplit l'espace autour du lecteur avec une affiche floue et assombrie au lieu de barres noires simples.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Rail d'épisodes « À suivre »",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Sur les fenêtres larges, ancre le lecteur de séries à gauche et affiche les épisodes à venir à côté.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Liste des chaînes en plein écran",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "En plein écran, placez la souris sur le bord gauche ou appuyez sur C pour faire glisser la liste des chaînes et changer de chaîne sans quitter le plein écran.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Liste des chaînes et des épisodes en plein écran",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Laissez la souris sur le bord gauche, cliquez dessus ou appuyez sur C en plein écran pour afficher la liste des chaînes d'un direct ou la liste des épisodes d'une série et changer sans quitter le plein écran.",
|
||||
"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",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Petite",
|
||||
"COVER_SIZE_MEDIUM": "Moyenne",
|
||||
"COVER_SIZE_LARGE": "Grande",
|
||||
"COVER_TITLES": "Afficher les titres sous les affiches",
|
||||
"COVER_TITLES_DESCRIPTION": "Désactivez cette option pour un mur d'affiches uniquement dans les grilles de films et de séries : le titre apparaît sur l'affiche lorsque vous la survolez ou lui donnez le focus. Les chaînes en direct, les résultats de recherche et les rangées du tableau de bord conservent leurs libellés.",
|
||||
"EPG_VIEW_MODE": "Vue du guide",
|
||||
"EPG_OFFSET_TITLE": "Décalage horaire de l’EPG",
|
||||
"EPG_OFFSET_HINT": "Décalez les horaires affichés des programmes sans actualiser le guide. Utilisez des minutes de -720 à 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Redémarrer l’application",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Ouvrir la release GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Nouveautés",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Aucune note de version n'a été trouvée pour {{version}} sur GitHub. La version a peut-être été supprimée ou n'est pas encore publiée.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Impossible de charger les notes de version.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Ouvrir la page des versions",
|
||||
"APP_UPDATE_CHANNEL": "Canal de mise à jour",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable suit les versions publiées. Nightly suit chaque fusion dans master et installe automatiquement les préversions. Appliqué à l’enregistrement.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Enregistrer et vérifier les mises à jour {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Ce résultat concerne le canal {{current}}. Enregistrez pour passer à {{next}} et vérifier à nouveau.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Cette mise à jour a été trouvée sur le canal {{current}}. Le canal {{next}} n'a pas encore été vérifié.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Les builds nightly sont des instantanés non testés de master. Ils peuvent être instables et leurs modifications de la base de données sont définitives : en revenant à Stable, ce build reste installé jusqu’à ce qu’une version stable plus récente existe. Sauvegardez d’abord vos listes de lecture.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Vous utilisez un build nightly. Le canal Stable proposera la prochaine version stable dès qu’elle sera plus récente que ce build.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV : moteur frame-copy (expérimental)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "La vidéo est rendue sur le canevas de l'application, les menus et boîtes de dialogue s'affichent donc au-dessus.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Prend effet après le redémarrage de l'application.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Quitter le plein écran",
|
||||
"CHANNEL_LIST": "Liste des chaînes",
|
||||
"HIDE_CHANNEL_LIST": "Masquer la liste des chaînes",
|
||||
"EPISODE_LIST": "Liste des épisodes",
|
||||
"HIDE_EPISODE_LIST": "Masquer la liste des épisodes",
|
||||
"SEARCH_CHANNELS": "Rechercher des chaînes",
|
||||
"SEARCH_IN": "Rechercher dans {{title}}",
|
||||
"CLEAR_SEARCH": "Effacer la recherche",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programmes",
|
||||
"LOADING": "Chargement",
|
||||
"QUEUED": "En file d'attente",
|
||||
"IMPORT_PANEL_MINIMIZE": "Réduire le panneau d’import EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Agrandir le panneau d’import EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} en cours",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} en attente",
|
||||
"TOTAL_CHANNELS": "Total des chaînes",
|
||||
"TOTAL_PROGRAMS": "Total des programmes",
|
||||
"UP_TO_DATE": "L'EPG est à jour, aucune synchronisation nécessaire",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Série marquée comme non vue",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Échec de la mise à jour de la série",
|
||||
"SERIES_WATCH_MENU": "Actions de visionnage de la série",
|
||||
"MOVIE_MARKED_WATCHED": "Marqué comme vu",
|
||||
"MOVIE_MARKED_UNWATCHED": "Marqué comme non vu",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Échec de la mise à jour de l’état « vu »",
|
||||
"EPISODES_WATCHED": "{{count}} sur {{total}} épisodes vus",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Métadonnées du portail indisponibles",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "À propos",
|
||||
"SEASONS_AND_EPISODES": "Saisons et épisodes",
|
||||
"SEASON_TAB": "Saison {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 épisode",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} épisodes",
|
||||
"BACK_TO_PLAYING_EPISODE": "Retour à l'épisode en cours",
|
||||
"GRID_VIEW": "Vue en grille",
|
||||
"LIST_VIEW": "Vue en liste",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Ce contenu n'est pas disponible sur ce serveur",
|
||||
"PLAYBACK_ERROR": "Échec de l'obtention de l'URL de lecture",
|
||||
"LOADING_SEASONS": "Chargement des saisons…",
|
||||
"LOADING_EPISODES": "Chargement des épisodes…",
|
||||
"EPISODES_LOAD_FAILED": "Les épisodes n'ont pas pu être chargés.",
|
||||
"LOADING_ALL_CHANNELS": "Chargement de la liste complète des chaînes…",
|
||||
"REFRESH_CHANNEL_LIST": "Actualiser la liste des chaînes",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Aucune chaîne dans cette catégorie",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Ouvrir les éléments récemment consultés",
|
||||
"SUBMIT_SHELL_SEARCH": "Lancer la recherche dans l’espace de travail",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Activer ou quitter le plein écran de la fenêtre",
|
||||
"ZOOM_IN_OUT": "Zoom avant ou arrière sur l'application",
|
||||
"RESET_ZOOM": "Réinitialiser le zoom de l'application",
|
||||
"TOGGLE_SIDEBAR": "Afficher ou masquer la barre latérale",
|
||||
"M3U_CHANNEL_NUMBER": "Sélectionner une chaîne M3U par numéro",
|
||||
"PLAY_PAUSE": "Lire ou mettre en pause la lecture MPV intégrée",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "EPG frissítése",
|
||||
"REFRESH_ALL_EPG": "Összes frissítése",
|
||||
"REMOVE_EPG_SOURCE": "EPG-forrás eltávolítása",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Naprakész",
|
||||
"EPG_STATUS_STALE": "Frissítendő",
|
||||
"EPG_STATUS_ERROR": "Sikertelen",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Nem sikerült törölni az EPG-adatokat. Próbálja újra.",
|
||||
"CLEARING_EPG_DATA": "Törlés…",
|
||||
"EPG_URL_ERROR": "A megadott érték nem érvényes URL-cím",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Az alapértelmezett videólejátszó kiválasztása",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Egységes vezérlők a webes lejátszókhoz",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Az IPTVnator közös vezérlőinek használata a HTML5-, Video.js- és ArtPlayer-lejátszóban.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Fill the space around the player with a blurred, dimmed poster instead of plain black bars.",
|
||||
"PLAYER_UP_NEXT_RAIL": "„Következik” epizódsáv",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Széles ablakban a sorozatlejátszó balra kerül, mellette pedig megjelennek a következő epizódok.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Csatornalista teljes képernyőn",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Teljes képernyőn vidd az egeret a bal szélre, vagy nyomd meg a C billentyűt: becsúszik a csatornalista, és a teljes képernyő elhagyása nélkül válthatsz csatornát.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Csatorna- és epizódlista teljes képernyőn",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Teljes képernyőn vidd az egeret a bal szélre, kattints rá, vagy nyomd meg a C billentyűt az élő adás csatornalistájának vagy a sorozat epizódlistájának megjelenítéséhez, és válts a teljes képernyő elhagyása nélkül.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Az alapértelmezett adatfolyam-formátum kiválasztása (Xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Az alkalmazás nyelvének kiválasztása",
|
||||
"THEME_DESCRIPTION": "Az alkalmazás megjelenési témájának kiválasztása",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Kicsi",
|
||||
"COVER_SIZE_MEDIUM": "Közepes",
|
||||
"COVER_SIZE_LARGE": "Nagy",
|
||||
"COVER_TITLES": "Borítók alatti címek megjelenítése",
|
||||
"COVER_TITLES_DESCRIPTION": "Kapcsolja ki, ha csak borítókból álló falat szeretne a film- és sorozatrácsokban: a cím a borítón jelenik meg, amikor rámutat az egérrel vagy arra fókuszál. Az élő csatornák, a keresési találatok és az irányítópulti sávok megtartják a feliratukat.",
|
||||
"EPG_VIEW_MODE": "Műsorújság nézete",
|
||||
"EPG_OFFSET_TITLE": "EPG-időeltolás",
|
||||
"EPG_OFFSET_HINT": "A műsorok megjelenített idejének eltolása a műsorújság frissítése nélkül. Percben adja meg, -720 és 720 között.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Alkalmazás újraindítása",
|
||||
"APP_UPDATE_OPEN_RELEASE": "GitHub-kiadás megnyitása",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Újdonságok",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "A(z) {{version}} verzióhoz nem található kiadási jegyzet a GitHubon. A kiadást talán eltávolították, vagy még nem tették közzé.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "A kiadási jegyzetek betöltése nem sikerült.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Kiadások oldalának megnyitása",
|
||||
"APP_UPDATE_CHANNEL": "Frissítési csatorna",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "A Stable a kiadott verziókat követi. A Nightly a master minden beolvasztását követi, és automatikusan telepíti az előzetes buildeket. Mentéskor lép érvénybe.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Mentés és {{channel}} frissítések ellenőrzése",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Ez az eredmény a(z) {{current}} csatornára vonatkozik. Mentse a(z) {{next}} csatornára váltáshoz, majd ellenőrizze újra.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Ez a frissítés a(z) {{current}} csatornán található. A(z) {{next}} csatorna még nincs ellenőrizve.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "A nightly buildek a master teszteletlen pillanatképei. Hibásak lehetnek, és adatbázis-módosításaik véglegesek: a Stable-re visszaváltva ez a build marad telepítve, amíg nem jelenik meg újabb stabil kiadás. Előbb készítsen biztonsági mentést a lejátszási listáiról.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Nightly buildet használ. A Stable csatorna a következő stabil kiadást kínálja fel, amint az újabb lesz ennél a buildnél.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Beágyazott MPV: képkocka-másolásos megjelenítés (kísérleti)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "A videó megjelenítése az alkalmazás rajzfelületén, hogy a menük és párbeszédpanelek fölötte jelenhessenek meg.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Az alkalmazás újraindítása után lép érvénybe.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Kilépés a teljes képernyős módból",
|
||||
"CHANNEL_LIST": "Csatornalista",
|
||||
"HIDE_CHANNEL_LIST": "Csatornalista elrejtése",
|
||||
"EPISODE_LIST": "Epizódlista",
|
||||
"HIDE_EPISODE_LIST": "Epizódlista elrejtése",
|
||||
"SEARCH_CHANNELS": "Csatornák keresése",
|
||||
"SEARCH_IN": "Keresés itt: {{title}}",
|
||||
"CLEAR_SEARCH": "Keresés törlése",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "műsor",
|
||||
"LOADING": "Betöltés",
|
||||
"QUEUED": "Várólistán",
|
||||
"IMPORT_PANEL_MINIMIZE": "EPG-importálási panel összecsukása",
|
||||
"IMPORT_PANEL_EXPAND": "EPG-importálási panel kibontása",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} betöltés alatt",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} várakozik",
|
||||
"TOTAL_CHANNELS": "Csatornák összesen",
|
||||
"TOTAL_PROGRAMS": "Műsorok összesen",
|
||||
"UP_TO_DATE": "Az EPG naprakész, nincs szükség szinkronizálásra",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Sorozat megjelölve nem megtekintettként",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Nem sikerült frissíteni a sorozatot",
|
||||
"SERIES_WATCH_MENU": "A sorozat megtekintési műveletei",
|
||||
"MOVIE_MARKED_WATCHED": "Megjelölve megtekintettként",
|
||||
"MOVIE_MARKED_UNWATCHED": "Megjelölve nem megtekintettként",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Nem sikerült frissíteni a megtekintési állapotot",
|
||||
"EPISODES_WATCHED": "Megtekintett epizódok: {{count}} / {{total}}",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "A portál metaadatai nem állnak rendelkezésre",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Ismertető",
|
||||
"SEASONS_AND_EPISODES": "Évadok és epizódok",
|
||||
"SEASON_TAB": "{{number}}. évad",
|
||||
"EPISODE_COUNT_ONE": "1 epizód",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} epizód",
|
||||
"BACK_TO_PLAYING_EPISODE": "Vissza a lejátszás alatt álló epizódhoz",
|
||||
"GRID_VIEW": "Rácsnézet",
|
||||
"LIST_VIEW": "Listanézet",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Ez a tartalom nem érhető el ezen a kiszolgálón",
|
||||
"PLAYBACK_ERROR": "Nem sikerült lekérni a lejátszáshoz szükséges URL-címet",
|
||||
"LOADING_SEASONS": "Évadok betöltése…",
|
||||
"LOADING_EPISODES": "Epizódok betöltése…",
|
||||
"EPISODES_LOAD_FAILED": "Az epizódokat nem sikerült betölteni.",
|
||||
"LOADING_ALL_CHANNELS": "Loading full channel list…",
|
||||
"REFRESH_CHANNEL_LIST": "Refresh channel list",
|
||||
"NO_CHANNELS_IN_CATEGORY": "No channels in this category",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Legutóbbi megtekintések megnyitása",
|
||||
"SUBMIT_SHELL_SEARCH": "Munkaterületi keresés indítása",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Az alkalmazásablak teljes képernyős módjának be- vagy kikapcsolása",
|
||||
"ZOOM_IN_OUT": "Az alkalmazás nagyítása vagy kicsinyítése",
|
||||
"RESET_ZOOM": "Az alkalmazás nagyításának visszaállítása",
|
||||
"TOGGLE_SIDEBAR": "Oldalsáv megjelenítése vagy elrejtése",
|
||||
"M3U_CHANNEL_NUMBER": "M3U-csatorna kiválasztása a számának beírásával",
|
||||
"PLAY_PAUSE": "A beágyazott MPV lejátszásának indítása vagy szüneteltetése",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "Aggiorna EPG",
|
||||
"REFRESH_ALL_EPG": "Aggiorna tutto",
|
||||
"REMOVE_EPG_SOURCE": "Rimuovi sorgente EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Aggiornato",
|
||||
"EPG_STATUS_STALE": "Da aggiornare",
|
||||
"EPG_STATUS_ERROR": "Errore",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Impossibile cancellare i dati EPG. Riprova.",
|
||||
"CLEARING_EPG_DATA": "Cancellazione…",
|
||||
"EPG_URL_ERROR": "Il valore non è una URL valida",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Seleziona lettore video predefinito",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Controlli unificati per i player web",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Usa i controlli condivisi di IPTVnator in HTML5, Video.js e ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Riempie lo spazio intorno al lettore con un poster sfocato e scurito invece delle semplici barre nere.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Pannello \"A seguire\" con i prossimi episodi",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Nelle finestre ampie, ancora il lettore delle serie a sinistra e mostra i prossimi episodi accanto.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Elenco canali a schermo intero",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "A schermo intero, porta il mouse sul bordo sinistro o premi C per far scorrere l'elenco dei canali e cambiare canale senza uscire dallo schermo intero.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Elenco canali ed episodi a schermo intero",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "A schermo intero, lascia il mouse sul bordo sinistro, fai clic su di esso o premi C per mostrare l'elenco dei canali di una diretta o l'elenco degli episodi di una serie e cambiare senza uscire dallo schermo intero.",
|
||||
"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",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Piccola",
|
||||
"COVER_SIZE_MEDIUM": "Media",
|
||||
"COVER_SIZE_LARGE": "Grande",
|
||||
"COVER_TITLES": "Mostra i titoli sotto le copertine",
|
||||
"COVER_TITLES_DESCRIPTION": "Disattiva per un muro di sole copertine nelle griglie di film e serie: il titolo appare sulla copertina quando ci passi sopra o la selezioni con la tastiera. I canali in diretta, i risultati di ricerca e le fasce della dashboard mantengono comunque le loro etichette.",
|
||||
"EPG_VIEW_MODE": "Vista guida",
|
||||
"EPG_OFFSET_TITLE": "Scostamento orario EPG",
|
||||
"EPG_OFFSET_HINT": "Sposta gli orari dei programmi visualizzati senza aggiornare la guida. Usa minuti da -720 a 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Riavvia applicazione",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Apri release GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Novità",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Nessuna nota di rilascio trovata per {{version}} su GitHub. La versione potrebbe essere stata rimossa o non è ancora pubblicata.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Impossibile caricare le note di rilascio.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Apri la pagina delle versioni",
|
||||
"APP_UPDATE_CHANNEL": "Canale di aggiornamento",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable segue le versioni pubblicate. Nightly segue ogni merge in master e installa automaticamente le build preliminari. Si applica al salvataggio.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Salva e controlla gli aggiornamenti {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Questo risultato riguarda il canale {{current}}. Salva per passare a {{next}} e controlla di nuovo.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Questo aggiornamento è stato trovato sul canale {{current}}. Il canale {{next}} non è ancora stato controllato.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Le build nightly sono istantanee non testate di master. Possono avere problemi e le loro modifiche al database sono permanenti: tornando a Stable, questa build resta installata finché non esiste una versione stabile più recente. Esegui prima un backup delle playlist.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Stai usando una build nightly. Il canale Stable proporrà la prossima versione stabile non appena sarà più recente di questa build.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: motore frame-copy (sperimentale)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Il video viene disegnato sul canvas dell'app, così menu e finestre di dialogo appaiono sopra di esso.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Ha effetto dopo il riavvio dell'app.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Esci dallo schermo intero",
|
||||
"CHANNEL_LIST": "Elenco canali",
|
||||
"HIDE_CHANNEL_LIST": "Nascondi l'elenco dei canali",
|
||||
"EPISODE_LIST": "Elenco episodi",
|
||||
"HIDE_EPISODE_LIST": "Nascondi l'elenco degli episodi",
|
||||
"SEARCH_CHANNELS": "Cerca canali",
|
||||
"SEARCH_IN": "Cerca in {{title}}",
|
||||
"CLEAR_SEARCH": "Cancella ricerca",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programmi",
|
||||
"LOADING": "Caricamento",
|
||||
"QUEUED": "In coda",
|
||||
"IMPORT_PANEL_MINIMIZE": "Riduci il pannello di importazione EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Espandi il pannello di importazione EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} in caricamento",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} in coda",
|
||||
"TOTAL_CHANNELS": "Totale canali",
|
||||
"TOTAL_PROGRAMS": "Totale programmi",
|
||||
"UP_TO_DATE": "L'EPG è aggiornato, nessuna sincronizzazione necessaria",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Serie segnata come non vista",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Aggiornamento della serie non riuscito",
|
||||
"SERIES_WATCH_MENU": "Azioni di visione della serie",
|
||||
"MOVIE_MARKED_WATCHED": "Segnato come visto",
|
||||
"MOVIE_MARKED_UNWATCHED": "Segnato come non visto",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Aggiornamento dello stato di visione non riuscito",
|
||||
"EPISODES_WATCHED": "{{count}} di {{total}} episodi visti",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Metadati del portale non disponibili",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Info",
|
||||
"SEASONS_AND_EPISODES": "Stagioni ed episodi",
|
||||
"SEASON_TAB": "Stagione {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 episodio",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} episodi",
|
||||
"BACK_TO_PLAYING_EPISODE": "Torna all'episodio in riproduzione",
|
||||
"GRID_VIEW": "Vista griglia",
|
||||
"LIST_VIEW": "Vista elenco",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Questo contenuto non è disponibile su questo server",
|
||||
"PLAYBACK_ERROR": "Impossibile ottenere l'URL di riproduzione",
|
||||
"LOADING_SEASONS": "Caricamento stagioni...",
|
||||
"LOADING_EPISODES": "Caricamento episodi...",
|
||||
"EPISODES_LOAD_FAILED": "Impossibile caricare gli episodi.",
|
||||
"LOADING_ALL_CHANNELS": "Caricamento dell'elenco completo dei canali…",
|
||||
"REFRESH_CHANNEL_LIST": "Aggiorna elenco canali",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Nessun canale in questa categoria",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Apri visti di recente",
|
||||
"SUBMIT_SHELL_SEARCH": "Invia ricerca nell’area di lavoro",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Attiva/disattiva schermo intero della finestra",
|
||||
"ZOOM_IN_OUT": "Ingrandisci o riduci l'applicazione",
|
||||
"RESET_ZOOM": "Reimposta lo zoom dell'applicazione",
|
||||
"TOGGLE_SIDEBAR": "Mostra/nascondi barra laterale",
|
||||
"M3U_CHANNEL_NUMBER": "Seleziona canale M3U per numero",
|
||||
"PLAY_PAUSE": "Riproduci o metti in pausa MPV integrato",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "EPGを更新",
|
||||
"REFRESH_ALL_EPG": "すべて更新",
|
||||
"REMOVE_EPG_SOURCE": "EPGソースを削除",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "最新",
|
||||
"EPG_STATUS_STALE": "更新が必要",
|
||||
"EPG_STATUS_ERROR": "失敗",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "EPGデータのクリアに失敗しました。もう一度お試しください。",
|
||||
"CLEARING_EPG_DATA": "クリア中…",
|
||||
"EPG_URL_ERROR": "有効なURLではありません",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "デフォルトのビデオプレーヤーを選択",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Webプレーヤーの統一コントロール",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "HTML5、Video.js、ArtPlayerでIPTVnator共通のコントロールを使用します。",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "プレーヤーの周囲をぼかしたポスター画像で埋め、通常の黒い枠の代わりに表示します。",
|
||||
"PLAYER_UP_NEXT_RAIL": "「次に再生」エピソードレール",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "ウィンドウ幅が広い場合、シリーズのプレーヤーを左側に配置し、その横に次のエピソードの一覧を表示します。",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "全画面表示でのチャンネルリスト",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "全画面表示中にマウスを左端に置くか C キーを押すと、チャンネルリストがスライド表示され、全画面を解除せずにチャンネルを切り替えられます。",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "全画面表示でのチャンネル・エピソードリスト",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "全画面表示中に左端にマウスを置く、クリックする、または C キーを押すと、ライブ配信のチャンネルリストやシリーズのエピソードリストが表示され、全画面表示のまま切り替えられます。",
|
||||
"STREAM_FORMAT_DESCRIPTION": "デフォルトのストリーム形式を選択(Xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "アプリケーションの言語を選択",
|
||||
"THEME_DESCRIPTION": "アプリケーションの視覚テーマを選択",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "小",
|
||||
"COVER_SIZE_MEDIUM": "中",
|
||||
"COVER_SIZE_LARGE": "大",
|
||||
"COVER_TITLES": "カバーの下にタイトルを表示",
|
||||
"COVER_TITLES_DESCRIPTION": "オフにすると、映画・シリーズのグリッドがポスターのみの表示になります。マウスを合わせるか、フォーカスするとカバーにタイトルが表示されます。ライブチャンネル、検索結果、ダッシュボードのレールでは常にラベルが表示されます。",
|
||||
"EPG_VIEW_MODE": "番組表の表示",
|
||||
"EPG_OFFSET_TITLE": "EPG 時刻の補正",
|
||||
"EPG_OFFSET_HINT": "番組表を更新せずに表示時刻をずらします。-720 から 720 までの分単位で指定してください。",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "アプリを再起動",
|
||||
"APP_UPDATE_OPEN_RELEASE": "GitHub リリースを開く",
|
||||
"APP_UPDATE_RELEASE_NOTES": "更新内容",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "{{version}} のリリースノートが GitHub に見つかりません。リリースが削除されたか、まだ公開されていない可能性があります。",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "リリースノートを読み込めませんでした。",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "リリースページを開く",
|
||||
"APP_UPDATE_CHANNEL": "更新チャンネル",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable は公開されたリリースに追従します。Nightly は master への各マージに追従し、プレリリースビルドを自動的にインストールします。保存時に適用されます。",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "保存して {{channel}} の更新を確認",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "この結果は {{current}} チャンネルのものです。保存して {{next}} に切り替え、もう一度確認してください。",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "この更新は {{current}} チャンネルで見つかったものです。{{next}} チャンネルはまだ確認されていません。",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly ビルドは master の未検証スナップショットです。動作しない場合があり、データベースの変更は元に戻せません。Stable に戻しても、より新しい安定版がリリースされるまでこのビルドが残ります。先にプレイリストをバックアップしてください。",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Nightly ビルドを使用中です。Stable チャンネルは、このビルドより新しい安定版がリリースされ次第、それを提案します。",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "埋め込みMPV:フレームコピーエンジン(実験的)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "映像をアプリのキャンバスに描画するため、メニューやダイアログをその上に表示できます。",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "アプリの再起動後に有効になります。",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "全画面表示を終了",
|
||||
"CHANNEL_LIST": "チャンネルリスト",
|
||||
"HIDE_CHANNEL_LIST": "チャンネルリストを隠す",
|
||||
"EPISODE_LIST": "エピソードリスト",
|
||||
"HIDE_EPISODE_LIST": "エピソードリストを隠す",
|
||||
"SEARCH_CHANNELS": "チャンネルを検索",
|
||||
"SEARCH_IN": "{{title}} 内を検索",
|
||||
"CLEAR_SEARCH": "検索をクリア",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "番組",
|
||||
"LOADING": "読み込み中",
|
||||
"QUEUED": "待機中",
|
||||
"IMPORT_PANEL_MINIMIZE": "EPG インポートパネルを最小化",
|
||||
"IMPORT_PANEL_EXPAND": "EPG インポートパネルを展開",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} 件読み込み中",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} 件待機中",
|
||||
"TOTAL_CHANNELS": "総チャンネル数",
|
||||
"TOTAL_PROGRAMS": "総番組数",
|
||||
"UP_TO_DATE": "EPGは最新です。同期は不要です",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "シリーズを未視聴にしました",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "シリーズの更新に失敗しました",
|
||||
"SERIES_WATCH_MENU": "シリーズの視聴操作",
|
||||
"MOVIE_MARKED_WATCHED": "視聴済みにしました",
|
||||
"MOVIE_MARKED_UNWATCHED": "未視聴にしました",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "視聴状態の更新に失敗しました",
|
||||
"EPISODES_WATCHED": "{{count}} / {{total}}エピソード視聴済み",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "ポータルのメタデータは利用できません",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "概要",
|
||||
"SEASONS_AND_EPISODES": "シーズンとエピソード",
|
||||
"SEASON_TAB": "シーズン{{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 エピソード",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} エピソード",
|
||||
"BACK_TO_PLAYING_EPISODE": "再生中のエピソードに戻る",
|
||||
"GRID_VIEW": "グリッド表示",
|
||||
"LIST_VIEW": "リスト表示",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "このコンテンツはこのサーバーでは利用できません",
|
||||
"PLAYBACK_ERROR": "再生URLの取得に失敗しました",
|
||||
"LOADING_SEASONS": "シーズンを読み込み中...",
|
||||
"LOADING_EPISODES": "エピソードを読み込み中...",
|
||||
"EPISODES_LOAD_FAILED": "エピソードを読み込めませんでした。",
|
||||
"LOADING_ALL_CHANNELS": "全チャンネルリストを読み込み中…",
|
||||
"REFRESH_CHANNEL_LIST": "チャンネルリストを更新",
|
||||
"NO_CHANNELS_IN_CATEGORY": "このカテゴリーにチャンネルがありません",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "最近視聴した項目を開く",
|
||||
"SUBMIT_SHELL_SEARCH": "ワークスペース検索を実行",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "アプリウィンドウの全画面表示を切り替え",
|
||||
"ZOOM_IN_OUT": "アプリを拡大または縮小",
|
||||
"RESET_ZOOM": "アプリのズームをリセット",
|
||||
"TOGGLE_SIDEBAR": "サイドバーを切り替え",
|
||||
"M3U_CHANNEL_NUMBER": "番号で M3U チャンネルを選択",
|
||||
"PLAY_PAUSE": "Embedded MPV の再生/一時停止",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "EPG 새로 고침",
|
||||
"REFRESH_ALL_EPG": "모두 새로 고침",
|
||||
"REMOVE_EPG_SOURCE": "EPG 소스 제거",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "최신",
|
||||
"EPG_STATUS_STALE": "새로 고침 필요",
|
||||
"EPG_STATUS_ERROR": "실패",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "EPG 데이터를 지우지 못했습니다. 다시 시도해 주세요.",
|
||||
"CLEARING_EPG_DATA": "지우는 중…",
|
||||
"EPG_URL_ERROR": "값이 유효한 URL이 아닙니다.",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "기본 비디오 플레이어 선택",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "웹 플레이어 통합 컨트롤",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "HTML5, Video.js 및 ArtPlayer에서 IPTVnator 공통 컨트롤을 사용합니다.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "플레이어 주변을 검은 막대 대신 흐려지고 어두워진 포스터로 채웁니다.",
|
||||
"PLAYER_UP_NEXT_RAIL": "다음 에피소드 레일",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "넓은 창에서 시리즈 플레이어를 왼쪽에 고정하고 그 옆에 다음 에피소드를 표시합니다.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "전체 화면에서 채널 목록",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "전체 화면에서 마우스를 왼쪽 가장자리에 두거나 C 키를 누르면 채널 목록이 나타나 전체 화면을 벗어나지 않고 채널을 바꿀 수 있습니다.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "전체 화면에서 채널 및 에피소드 목록",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "전체 화면에서 마우스를 왼쪽 가장자리에 두거나 클릭하거나 C 키를 누르면 라이브 방송의 채널 목록이나 시리즈의 에피소드 목록이 나타나 전체 화면을 벗어나지 않고 전환할 수 있습니다.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "기본 스트림 형식 선택 (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "애플리케이션 언어 선택",
|
||||
"THEME_DESCRIPTION": "애플리케이션의 시각적 테마 선택",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "작게",
|
||||
"COVER_SIZE_MEDIUM": "중간",
|
||||
"COVER_SIZE_LARGE": "크게",
|
||||
"COVER_TITLES": "커버 아래에 제목 표시",
|
||||
"COVER_TITLES_DESCRIPTION": "끄면 영화 및 시리즈 그리드가 포스터만 표시되는 화면이 됩니다. 마우스로 가리키거나 키보드로 포커스하면 커버 위에 제목이 나타납니다. 라이브 채널, 검색 결과, 대시보드 레일은 항상 레이블을 유지합니다.",
|
||||
"EPG_VIEW_MODE": "가이드 보기",
|
||||
"EPG_OFFSET_TITLE": "EPG 시간 오프셋",
|
||||
"EPG_OFFSET_HINT": "가이드를 새로 고치지 않고 표시되는 프로그램 시간을 조정합니다. -720에서 720 사이의 분 단위로 입력하세요.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "애플리케이션 다시 시작",
|
||||
"APP_UPDATE_OPEN_RELEASE": "GitHub 릴리스 열기",
|
||||
"APP_UPDATE_RELEASE_NOTES": "새로운 내용",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "GitHub에서 {{version}}의 릴리스 노트를 찾을 수 없습니다. 릴리스가 삭제되었거나 아직 게시되지 않았을 수 있습니다.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "릴리스 노트를 불러올 수 없습니다.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "릴리스 페이지 열기",
|
||||
"APP_UPDATE_CHANNEL": "업데이트 채널",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable은 정식 릴리스를 따릅니다. Nightly는 master에 병합될 때마다 따라가며 프리릴리스 빌드를 자동으로 설치합니다. 저장 시 적용됩니다.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "저장하고 {{channel}} 업데이트 확인",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "이 결과는 {{current}} 채널에 대한 것입니다. 저장하여 {{next}}(으)로 전환한 뒤 다시 확인하세요.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "이 업데이트는 {{current}} 채널에서 찾은 것입니다. {{next}} 채널은 아직 확인되지 않았습니다.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly 빌드는 테스트되지 않은 master의 스냅샷입니다. 오류가 있을 수 있으며 데이터베이스 변경은 되돌릴 수 없습니다. Stable로 되돌려도 더 새로운 안정 릴리스가 나올 때까지 이 빌드가 유지됩니다. 먼저 재생목록을 백업하세요.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Nightly 빌드를 사용 중입니다. Stable 채널은 이 빌드보다 새로운 안정 릴리스가 나오면 그것을 제안합니다.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "내장 MPV: 프레임 복사 엔진(실험적)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "비디오를 앱 캔버스에 렌더링하여 메뉴와 대화 상자가 그 위에 표시됩니다.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "앱을 다시 시작한 후 적용됩니다.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "전체 화면 종료",
|
||||
"CHANNEL_LIST": "채널 목록",
|
||||
"HIDE_CHANNEL_LIST": "채널 목록 숨기기",
|
||||
"EPISODE_LIST": "에피소드 목록",
|
||||
"HIDE_EPISODE_LIST": "에피소드 목록 숨기기",
|
||||
"SEARCH_CHANNELS": "채널 검색",
|
||||
"SEARCH_IN": "{{title}}에서 검색",
|
||||
"CLEAR_SEARCH": "검색 지우기",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "프로그램",
|
||||
"LOADING": "불러오는 중",
|
||||
"QUEUED": "대기 중",
|
||||
"IMPORT_PANEL_MINIMIZE": "EPG 가져오기 패널 최소화",
|
||||
"IMPORT_PANEL_EXPAND": "EPG 가져오기 패널 펼치기",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}}개 불러오는 중",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}}개 대기 중",
|
||||
"TOTAL_CHANNELS": "전체 채널",
|
||||
"TOTAL_PROGRAMS": "전체 프로그램",
|
||||
"UP_TO_DATE": "EPG가 최신 상태이며 동기화가 필요하지 않습니다",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "시리즈가 시청하지 않은 것으로 표시되었습니다",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "시리즈 업데이트에 실패했습니다",
|
||||
"SERIES_WATCH_MENU": "시리즈 시청 작업",
|
||||
"MOVIE_MARKED_WATCHED": "시청한 것으로 표시되었습니다",
|
||||
"MOVIE_MARKED_UNWATCHED": "시청하지 않은 것으로 표시되었습니다",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "시청 상태 업데이트에 실패했습니다",
|
||||
"EPISODES_WATCHED": "{{total}}개 중 {{count}}개 에피소드 시청",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "포털 메타데이터를 사용할 수 없습니다",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "정보",
|
||||
"SEASONS_AND_EPISODES": "시즌 및 에피소드",
|
||||
"SEASON_TAB": "시즌 {{number}}",
|
||||
"EPISODE_COUNT_ONE": "에피소드 1개",
|
||||
"EPISODE_COUNT_OTHER": "에피소드 {{count}}개",
|
||||
"BACK_TO_PLAYING_EPISODE": "재생 중인 에피소드로 돌아가기",
|
||||
"GRID_VIEW": "그리드 보기",
|
||||
"LIST_VIEW": "목록 보기",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "이 서버에서는 이 콘텐츠를 사용할 수 없습니다",
|
||||
"PLAYBACK_ERROR": "재생 URL을 가져오지 못했습니다",
|
||||
"LOADING_SEASONS": "시즌을 불러오는 중...",
|
||||
"LOADING_EPISODES": "에피소드를 불러오는 중...",
|
||||
"EPISODES_LOAD_FAILED": "에피소드를 불러올 수 없습니다.",
|
||||
"LOADING_ALL_CHANNELS": "전체 채널 목록 불러오는 중…",
|
||||
"REFRESH_CHANNEL_LIST": "채널 목록 새로고침",
|
||||
"NO_CHANNELS_IN_CATEGORY": "이 카테고리에 채널이 없습니다",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "최근 시청 항목 열기",
|
||||
"SUBMIT_SHELL_SEARCH": "워크스페이스 검색 실행",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "앱 창 전체 화면 전환",
|
||||
"ZOOM_IN_OUT": "앱 확대 또는 축소",
|
||||
"RESET_ZOOM": "앱 확대/축소 초기화",
|
||||
"TOGGLE_SIDEBAR": "사이드바 전환",
|
||||
"M3U_CHANNEL_NUMBER": "번호로 M3U 채널 선택",
|
||||
"PLAY_PAUSE": "Embedded MPV 재생 또는 일시정지",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "EPG vernieuwen",
|
||||
"REFRESH_ALL_EPG": "Alles vernieuwen",
|
||||
"REMOVE_EPG_SOURCE": "EPG-bron verwijderen",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Up-to-date",
|
||||
"EPG_STATUS_STALE": "Verouderd",
|
||||
"EPG_STATUS_ERROR": "Mislukt",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Wissen van EPG-gegevens mislukt. Probeer het opnieuw.",
|
||||
"CLEARING_EPG_DATA": "Wissen…",
|
||||
"EPG_URL_ERROR": "De opgegeven waarde is geen geldige URL.",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Selecteer de standaard videospeler",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Uniforme bediening voor webspelers",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Gebruik de gedeelde IPTVnator-bediening in HTML5, Video.js en ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Vult de ruimte rond de speler met een vervaagde, gedimde posterafbeelding in plaats van zwarte balken.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Hierna-paneel met afleveringen",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Zet de seriespeler in brede vensters links vast en toont de komende afleveringen ernaast.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Zenderlijst in volledig scherm",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Beweeg in volledig scherm de muis naar de linkerrand of druk op C om de zenderlijst in te schuiven en van zender te wisselen zonder het volledige scherm te verlaten.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Zender- en afleveringenlijst in volledig scherm",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Laat de muis op de linkerrand rusten, klik erop of druk op C in volledig scherm om de zenderlijst van een live-uitzending of de afleveringenlijst van een serie te tonen en te wisselen zonder het volledige scherm te verlaten.",
|
||||
"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",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Klein",
|
||||
"COVER_SIZE_MEDIUM": "Gemiddeld",
|
||||
"COVER_SIZE_LARGE": "Groot",
|
||||
"COVER_TITLES": "Titels onder hoezen tonen",
|
||||
"COVER_TITLES_DESCRIPTION": "Schakel dit uit voor een muur met alleen posters in rasters met films en reeksen: de titel verschijnt op de hoes wanneer u de muis erover beweegt of hem focust. Livekanalen, zoekresultaten en dashboardrails behouden hun labels.",
|
||||
"EPG_VIEW_MODE": "Gidsweergave",
|
||||
"EPG_OFFSET_TITLE": "EPG-tijdverschuiving",
|
||||
"EPG_OFFSET_HINT": "Verschuif weergegeven programmatijden zonder de gids te vernieuwen. Gebruik minuten van -720 tot 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Applicatie opnieuw starten",
|
||||
"APP_UPDATE_OPEN_RELEASE": "GitHub-release openen",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Wat is nieuw",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Er zijn geen releasenotes voor {{version}} gevonden op GitHub. De release is mogelijk verwijderd of nog niet gepubliceerd.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "De releasenotes konden niet worden geladen.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Releasepagina openen",
|
||||
"APP_UPDATE_CHANNEL": "Updatekanaal",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable volgt de uitgebrachte releases. Nightly volgt elke merge in master en installeert voorlopige builds automatisch. Wordt toegepast bij opslaan.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Opslaan en controleren op {{channel}}-updates",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Dit resultaat geldt voor het kanaal {{current}}. Sla op om naar {{next}} te wisselen en controleer opnieuw.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Deze update is gevonden op het kanaal {{current}}. Het kanaal {{next}} is nog niet gecontroleerd.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly-builds zijn ongeteste momentopnamen van master. Ze kunnen fouten bevatten en hun databasewijzigingen zijn permanent: na terugschakelen naar Stable blijft deze build geïnstalleerd tot er een nieuwere stabiele release is. Maak eerst een back-up van je afspeellijsten.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Je gebruikt een nightly-build. Het Stable-kanaal biedt de volgende stabiele release aan zodra die nieuwer is dan deze build.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: frame-copy-engine (experimenteel)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "De video wordt op het app-canvas getekend zodat menu's en dialoogvensters erboven verschijnen.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Wordt van kracht na het herstarten van de app.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Volledig scherm sluiten",
|
||||
"CHANNEL_LIST": "Zenderlijst",
|
||||
"HIDE_CHANNEL_LIST": "Zenderlijst verbergen",
|
||||
"EPISODE_LIST": "Afleveringenlijst",
|
||||
"HIDE_EPISODE_LIST": "Afleveringenlijst verbergen",
|
||||
"SEARCH_CHANNELS": "Zenders zoeken",
|
||||
"SEARCH_IN": "Zoeken in {{title}}",
|
||||
"CLEAR_SEARCH": "Zoekopdracht wissen",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programma's",
|
||||
"LOADING": "Laden",
|
||||
"QUEUED": "In wachtrij",
|
||||
"IMPORT_PANEL_MINIMIZE": "EPG-importpaneel minimaliseren",
|
||||
"IMPORT_PANEL_EXPAND": "EPG-importpaneel uitvouwen",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} wordt geladen",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} in de wachtrij",
|
||||
"TOTAL_CHANNELS": "Totaal aantal kanalen",
|
||||
"TOTAL_PROGRAMS": "Totaal aantal programma's",
|
||||
"UP_TO_DATE": "EPG is up-to-date, synchronisatie niet nodig",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Serie gemarkeerd als niet bekeken",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Bijwerken van de serie is mislukt",
|
||||
"SERIES_WATCH_MENU": "Kijkacties voor serie",
|
||||
"MOVIE_MARKED_WATCHED": "Gemarkeerd als bekeken",
|
||||
"MOVIE_MARKED_UNWATCHED": "Gemarkeerd als niet bekeken",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Bijwerken van de kijkstatus is mislukt",
|
||||
"EPISODES_WATCHED": "{{count}} van {{total}} afleveringen bekeken",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Portaalmetadata niet beschikbaar",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Over",
|
||||
"SEASONS_AND_EPISODES": "Seizoenen en afleveringen",
|
||||
"SEASON_TAB": "Seizoen {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 aflevering",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} afleveringen",
|
||||
"BACK_TO_PLAYING_EPISODE": "Terug naar lopende aflevering",
|
||||
"GRID_VIEW": "Rasterweergave",
|
||||
"LIST_VIEW": "Lijstweergave",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Deze inhoud is niet beschikbaar op deze server",
|
||||
"PLAYBACK_ERROR": "Ophalen van afspeel-URL mislukt",
|
||||
"LOADING_SEASONS": "Seizoenen laden...",
|
||||
"LOADING_EPISODES": "Afleveringen laden...",
|
||||
"EPISODES_LOAD_FAILED": "De afleveringen konden niet worden geladen.",
|
||||
"LOADING_ALL_CHANNELS": "Volledige zenderlijst wordt geladen…",
|
||||
"REFRESH_CHANNEL_LIST": "Zenderlijst vernieuwen",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Geen zenders in deze categorie",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Recent bekeken openen",
|
||||
"SUBMIT_SHELL_SEARCH": "Werkruimtezoekopdracht uitvoeren",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Volledig scherm voor het app-venster in- of uitschakelen",
|
||||
"ZOOM_IN_OUT": "App in- of uitzoomen",
|
||||
"RESET_ZOOM": "App-zoom herstellen",
|
||||
"TOGGLE_SIDEBAR": "Zijbalk in- of uitschakelen",
|
||||
"M3U_CHANNEL_NUMBER": "M3U-kanaal selecteren op nummer",
|
||||
"PLAY_PAUSE": "Ingebedde MPV-weergave afspelen of pauzeren",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "Odśwież EPG",
|
||||
"REFRESH_ALL_EPG": "Odśwież wszystko",
|
||||
"REMOVE_EPG_SOURCE": "Usuń źródło EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Aktualne",
|
||||
"EPG_STATUS_STALE": "Wymaga odświeżenia",
|
||||
"EPG_STATUS_ERROR": "Błąd",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Nie udało się wyczyścić danych EPG. Spróbuj ponownie.",
|
||||
"CLEARING_EPG_DATA": "Czyszczenie…",
|
||||
"EPG_URL_ERROR": "Wartość nie jest prawidłowym URL",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Wybierz domyślny odtwarzacz wideo",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Ujednolicone sterowanie odtwarzaczami internetowymi",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Używaj wspólnych elementów sterowania IPTVnator w HTML5, Video.js i ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Wypełnij przestrzeń wokół odtwarzacza rozmytym, przyciemnianym plakatem zamiast zwykłych czarnych pasków.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Panel następnych odcinków",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "W szerokich oknach zadokuj odtwarzacz serialu po lewej stronie i pokaż obok kolejne odcinki.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Lista kanałów na pełnym ekranie",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Na pełnym ekranie przesuń mysz do lewej krawędzi lub naciśnij C, aby wysunąć listę kanałów i zmienić kanał bez wychodzenia z pełnego ekranu.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Lista kanałów i odcinków w trybie pełnoekranowym",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "W trybie pełnoekranowym przytrzymaj mysz przy lewej krawędzi, kliknij ją lub naciśnij C, aby wysunąć listę kanałów transmisji na żywo lub listę odcinków serialu i przełączać bez wychodzenia z pełnego ekranu.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Wybierz domyślny format strumienia (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Wybierz język aplikacji",
|
||||
"THEME_DESCRIPTION": "Wybierz motyw wizualny aplikacji",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Mały",
|
||||
"COVER_SIZE_MEDIUM": "Średni",
|
||||
"COVER_SIZE_LARGE": "Duży",
|
||||
"COVER_TITLES": "Pokaż tytuły pod okładkami",
|
||||
"COVER_TITLES_DESCRIPTION": "Wyłącz, aby uzyskać ścianę samych plakatów w siatkach filmów i seriali: tytuł pojawia się na okładce po najechaniu na nią kursorem lub przejściu do niej klawiaturą. Kanały na żywo, wyniki wyszukiwania i sekcje pulpitu nadal pokazują tytuły.",
|
||||
"EPG_VIEW_MODE": "Widok przewodnika",
|
||||
"EPG_OFFSET_TITLE": "Przesunięcie czasu EPG",
|
||||
"EPG_OFFSET_HINT": "Przesuń wyświetlane godziny programów bez odświeżania przewodnika. Użyj minut od -720 do 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Uruchom aplikację ponownie",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Otwórz wydanie w GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Co nowego",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Nie znaleziono informacji o wydaniu {{version}} w GitHub. Wydanie mogło zostać usunięte lub nie zostało jeszcze opublikowane.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Nie udało się wczytać informacji o wydaniu.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Otwórz stronę wydań",
|
||||
"APP_UPDATE_CHANNEL": "Kanał aktualizacji",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable podąża za opublikowanymi wydaniami. Nightly podąża za każdym scaleniem do master i automatycznie instaluje wersje wstępne. Obowiązuje po zapisaniu.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Zapisz i sprawdź aktualizacje {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Ten wynik dotyczy kanału {{current}}. Zapisz, aby przełączyć na {{next}} i sprawdzić ponownie.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Ta aktualizacja została znaleziona na kanale {{current}}. Kanał {{next}} nie został jeszcze sprawdzony.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Kompilacje nightly to nieprzetestowane migawki master. Mogą działać niestabilnie, a ich zmiany w bazie danych są trwałe: po powrocie na Stable ta kompilacja pozostanie zainstalowana, dopóki nie pojawi się nowsze wydanie stabilne. Najpierw wykonaj kopię zapasową playlist.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Używasz kompilacji nightly. Kanał Stable zaproponuje następne wydanie stabilne, gdy tylko będzie nowsze od tej kompilacji.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: silnik frame-copy (eksperymentalny)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Wideo jest rysowane na kanwie aplikacji, dzięki czemu menu i okna dialogowe wyświetlają się nad nim.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Zacznie działać po ponownym uruchomieniu aplikacji.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Wyłącz pełny ekran",
|
||||
"CHANNEL_LIST": "Lista kanałów",
|
||||
"HIDE_CHANNEL_LIST": "Ukryj listę kanałów",
|
||||
"EPISODE_LIST": "Lista odcinków",
|
||||
"HIDE_EPISODE_LIST": "Ukryj listę odcinków",
|
||||
"SEARCH_CHANNELS": "Szukaj kanałów",
|
||||
"SEARCH_IN": "Szukaj w {{title}}",
|
||||
"CLEAR_SEARCH": "Wyczyść wyszukiwanie",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programów",
|
||||
"LOADING": "Ładowanie",
|
||||
"QUEUED": "W kolejce",
|
||||
"IMPORT_PANEL_MINIMIZE": "Zwiń panel importu EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Rozwiń panel importu EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "Wczytywanie: {{count}}",
|
||||
"IMPORT_SUMMARY_QUEUED": "w kolejce: {{count}}",
|
||||
"TOTAL_CHANNELS": "Wszystkie kanały",
|
||||
"TOTAL_PROGRAMS": "Wszystkie programy",
|
||||
"UP_TO_DATE": "EPG jest aktualne, synchronizacja nie jest potrzebna",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Oznaczono serial jako nieobejrzany",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Nie udało się zaktualizować serialu",
|
||||
"SERIES_WATCH_MENU": "Akcje oglądania serialu",
|
||||
"MOVIE_MARKED_WATCHED": "Oznaczono jako obejrzane",
|
||||
"MOVIE_MARKED_UNWATCHED": "Oznaczono jako nieobejrzane",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Nie udało się zaktualizować stanu obejrzenia",
|
||||
"EPISODES_WATCHED": "Obejrzano {{count}} z {{total}} odcinków",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Metadane portalu niedostępne",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Informacje",
|
||||
"SEASONS_AND_EPISODES": "Sezony i odcinki",
|
||||
"SEASON_TAB": "Sezon {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 odcinek",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} odcinków",
|
||||
"BACK_TO_PLAYING_EPISODE": "Wróć do odtwarzanego odcinka",
|
||||
"GRID_VIEW": "Widok siatki",
|
||||
"LIST_VIEW": "Widok listy",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Ta treść jest niedostępna na tym serwerze",
|
||||
"PLAYBACK_ERROR": "Nie udało się uzyskać URL odtwarzania",
|
||||
"LOADING_SEASONS": "Ładowanie sezonów...",
|
||||
"LOADING_EPISODES": "Ładowanie odcinków...",
|
||||
"EPISODES_LOAD_FAILED": "Nie udało się załadować odcinków.",
|
||||
"LOADING_ALL_CHANNELS": "Ładowanie pełnej listy kanałów…",
|
||||
"REFRESH_CHANNEL_LIST": "Odśwież listę kanałów",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Brak kanałów w tej kategorii",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Otwórz ostatnio oglądane",
|
||||
"SUBMIT_SHELL_SEARCH": "Uruchom wyszukiwanie w obszarze roboczym",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Przełącz pełny ekran okna aplikacji",
|
||||
"ZOOM_IN_OUT": "Powiększ lub pomniejsz aplikację",
|
||||
"RESET_ZOOM": "Zresetuj powiększenie aplikacji",
|
||||
"TOGGLE_SIDEBAR": "Przełącz pasek boczny",
|
||||
"M3U_CHANNEL_NUMBER": "Wybierz kanał M3U według numeru",
|
||||
"PLAY_PAUSE": "Odtwórz lub wstrzymaj osadzony MPV",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "Atualizar EPG",
|
||||
"REFRESH_ALL_EPG": "Atualizar tudo",
|
||||
"REMOVE_EPG_SOURCE": "Remover fonte EPG",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Atualizado",
|
||||
"EPG_STATUS_STALE": "Necessita atualização",
|
||||
"EPG_STATUS_ERROR": "Falha",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "Falha ao limpar os dados de EPG. Tente novamente.",
|
||||
"CLEARING_EPG_DATA": "Limpando…",
|
||||
"EPG_URL_ERROR": "O valor não é uma URL válida",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Selecionar reprodutor de vídeo padrão",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Controlos unificados para leitores web",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Usa os controlos partilhados do IPTVnator em HTML5, Video.js e ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Preenche o espaço ao redor do reprodutor com um pôster desfocado e escurecido em vez de barras pretas simples.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Painel de próximos episódios",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Em janelas largas, ancora o reprodutor de séries à esquerda e mostra os próximos episódios ao lado.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Lista de canais em tela cheia",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Em tela cheia, leve o mouse até a borda esquerda ou pressione C para deslizar a lista de canais e trocar de canal sem sair da tela cheia.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Lista de canais e episódios em tela cheia",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Deixe o mouse na borda esquerda, clique nela ou pressione C em tela cheia para mostrar a lista de canais de uma transmissão ao vivo ou a lista de episódios de uma série e trocar sem sair da tela cheia.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Selecionar formato de stream padrão (xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "Selecionar idioma do aplicativo",
|
||||
"THEME_DESCRIPTION": "Selecionar tema visual do aplicativo",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Pequeno",
|
||||
"COVER_SIZE_MEDIUM": "Médio",
|
||||
"COVER_SIZE_LARGE": "Grande",
|
||||
"COVER_TITLES": "Mostrar títulos sob as capas",
|
||||
"COVER_TITLES_DESCRIPTION": "Desative para um mural apenas de pôsteres nas grades de filmes e séries: o título aparece sobre a capa ao passar o cursor ou focar nela. Canais ao vivo, resultados de pesquisa e os trilhos do dashboard mantêm os seus rótulos.",
|
||||
"EPG_VIEW_MODE": "Visualização do guia",
|
||||
"EPG_OFFSET_TITLE": "Deslocamento de horário do EPG",
|
||||
"EPG_OFFSET_HINT": "Desloque os horários dos programas exibidos sem atualizar o guia. Use minutos de -720 a 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Reiniciar aplicativo",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Abrir release no GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Novidades",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Nenhuma nota de versão foi encontrada para {{version}} no GitHub. A versão pode ter sido removida ou ainda não foi publicada.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Não foi possível carregar as notas da versão.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Abrir página de versões",
|
||||
"APP_UPDATE_CHANNEL": "Canal de atualização",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable segue as versões publicadas. Nightly segue cada merge em master e instala compilações preliminares automaticamente. Aplica-se ao salvar.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Salvar e verificar atualizações de {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Este resultado é do canal {{current}}. Salve para mudar para {{next}} e verificar novamente.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Esta atualização foi encontrada no canal {{current}}. O canal {{next}} ainda não foi verificado.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "As compilações nightly são instantâneos não testados de master. Podem falhar e suas alterações no banco de dados são permanentes: ao voltar para Stable, esta compilação permanece instalada até existir uma versão estável mais recente. Faça backup das suas playlists primeiro.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Você está usando uma compilação nightly. O canal Stable oferecerá a próxima versão estável assim que ela for mais recente que esta compilação.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: motor frame-copy (experimental)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "O vídeo é desenhado na tela da aplicação, para que menus e diálogos fiquem por cima.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Entra em vigor após reiniciar a aplicação.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Sair da tela cheia",
|
||||
"CHANNEL_LIST": "Lista de canais",
|
||||
"HIDE_CHANNEL_LIST": "Ocultar lista de canais",
|
||||
"EPISODE_LIST": "Lista de episódios",
|
||||
"HIDE_EPISODE_LIST": "Ocultar lista de episódios",
|
||||
"SEARCH_CHANNELS": "Pesquisar canais",
|
||||
"SEARCH_IN": "Pesquisar em {{title}}",
|
||||
"CLEAR_SEARCH": "Limpar pesquisa",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programas",
|
||||
"LOADING": "Carregando",
|
||||
"QUEUED": "Na fila",
|
||||
"IMPORT_PANEL_MINIMIZE": "Minimizar o painel de importação do EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Expandir o painel de importação do EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} a carregar",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} na fila",
|
||||
"TOTAL_CHANNELS": "Total de canais",
|
||||
"TOTAL_PROGRAMS": "Total de programas",
|
||||
"UP_TO_DATE": "EPG está atualizado, sincronização não é necessária",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Série marcada como não assistida",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "A atualização da série falhou",
|
||||
"SERIES_WATCH_MENU": "Ações de visualização da série",
|
||||
"MOVIE_MARKED_WATCHED": "Marcado como assistido",
|
||||
"MOVIE_MARKED_UNWATCHED": "Marcado como não assistido",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "A atualização do estado de assistido falhou",
|
||||
"EPISODES_WATCHED": "{{count}} de {{total}} episódios assistidos",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Metadados do portal indisponíveis",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Sobre",
|
||||
"SEASONS_AND_EPISODES": "Temporadas e episódios",
|
||||
"SEASON_TAB": "Temporada {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 episódio",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} episódios",
|
||||
"BACK_TO_PLAYING_EPISODE": "Voltar ao episódio em reprodução",
|
||||
"GRID_VIEW": "Visualização em grade",
|
||||
"LIST_VIEW": "Visualização em lista",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Este conteúdo não está disponível neste servidor",
|
||||
"PLAYBACK_ERROR": "Falha ao obter URL de reprodução",
|
||||
"LOADING_SEASONS": "Carregando temporadas...",
|
||||
"LOADING_EPISODES": "Carregando episódios...",
|
||||
"EPISODES_LOAD_FAILED": "Não foi possível carregar os episódios.",
|
||||
"LOADING_ALL_CHANNELS": "A carregar a lista completa de canais…",
|
||||
"REFRESH_CHANNEL_LIST": "Atualizar lista de canais",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Nenhum canal nesta categoria",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Abrir assistidos recentemente",
|
||||
"SUBMIT_SHELL_SEARCH": "Enviar pesquisa da área de trabalho",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Alternar tela cheia da janela do aplicativo",
|
||||
"ZOOM_IN_OUT": "Aumentar ou diminuir o zoom do aplicativo",
|
||||
"RESET_ZOOM": "Redefinir o zoom do aplicativo",
|
||||
"TOGGLE_SIDEBAR": "Alternar barra lateral",
|
||||
"M3U_CHANNEL_NUMBER": "Selecionar canal M3U por número",
|
||||
"PLAY_PAUSE": "Reproduzir ou pausar reprodução do Embedded MPV",
|
||||
|
||||
@@ -268,10 +268,10 @@
|
||||
"GENERAL_SUBTITLE": "Изменение основных параметров приложения",
|
||||
"PLAYBACK": "Воспроизведение",
|
||||
"EPG_SOURCES": "Источники EPG",
|
||||
"EPG_SOURCES_DESCRIPTION": "Добавьте один или несколько URL-адресов источников EPG",
|
||||
"EPG_SOURCES_DESCRIPTION": "Добавьте один или несколько источников XMLTV: ссылки или локальные файлы",
|
||||
"PLAYLIST_EPG_SOURCES_AUTO_SELECTED": "{{enabled}} из {{detected}} обнаруженных источников EPG плейлиста были автоматически выбраны для импорта.",
|
||||
"EPG_EMPTY_TITLE": "Источников EPG пока нет",
|
||||
"EPG_EMPTY_HINT": "Добавьте URL, чтобы получить программу передач для каналов.",
|
||||
"EPG_EMPTY_HINT": "Добавьте ссылку или локальный XMLTV-файл, чтобы получить программу передач для каналов.",
|
||||
"PREFER_UPLOADED_EPG_TITLE": "Предпочитать ваши источники EPG для плейлистов Xtream",
|
||||
"PREFER_UPLOADED_EPG_HINT": "По умолчанию плейлисты Xtream используют программу передач от вашего провайдера, а ваши источники EPG заполняют только недостающие каналы. Включите, чтобы сначала использовать ваш загруженный EPG.",
|
||||
"DATA": "Данные",
|
||||
@@ -314,11 +314,12 @@
|
||||
"HERO_DESCRIPTION": "Настраивайте воспроизведение, телегид, удалённый доступ и работу с данными в одном месте, не теряя стиль текущего рабочего пространства.",
|
||||
"SUBTITLE": "Здесь можно изменить общие настройки приложения",
|
||||
"DESCRIPTION": "Закончив настройку, не забудьте подтвердить новые настройки кнопкой 'Сохранить'",
|
||||
"EPG_URL_LABEL": "Ссылка на программу каналов (EPG) (*.xml или *.xml.gz)",
|
||||
"EPG_URL_PLACEHOLDER": "Введите ссылку",
|
||||
"EPG_URL_LABEL": "Источник EPG (ссылка или локальный файл, *.xml или *.xml.gz)",
|
||||
"EPG_URL_PLACEHOLDER": "Введите ссылку или путь к файлу",
|
||||
"REFRESH_EPG": "Обновить программу каналов (EPG)",
|
||||
"REFRESH_ALL_EPG": "Обновить все",
|
||||
"REMOVE_EPG_SOURCE": "Удалить источник EPG",
|
||||
"BROWSE_EPG_FILE": "Выбрать локальный XMLTV-файл…",
|
||||
"EPG_STATUS_FRESH": "Актуально",
|
||||
"EPG_STATUS_STALE": "Требует обновления",
|
||||
"EPG_STATUS_ERROR": "Ошибка",
|
||||
@@ -363,7 +364,10 @@
|
||||
"EPG_DATA_CLEARED": "Данные EPG очищены",
|
||||
"EPG_DATA_CLEAR_FAILED": "Не удалось очистить данные EPG. Попробуйте ещё раз.",
|
||||
"CLEARING_EPG_DATA": "Очистка…",
|
||||
"EPG_URL_ERROR": "Введенное значение не является URL-адресом.",
|
||||
"EPG_URL_ERROR": "Введите ссылку http(s), абсолютный путь к файлу или URL вида file://",
|
||||
"EPG_SOURCE_LIST_TITLE": "Список источников",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Поддерживаются как ссылки на XMLTV (http/https), так и файлы на этом компьютере, в виде обычного .xml или сжатого .xml.gz. Для локального файла укажите абсолютный путь или URL вида file://.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Примеры:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Выбрать стандартный видео плеер",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Единые элементы управления для веб-плееров",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "Использовать общие элементы управления IPTVnator в HTML5, Video.js и ArtPlayer.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Заполнять пространство вокруг плеера размытым затемнённым постером вместо чёрных полос.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Панель следующих серий",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "В широких окнах закреплять плеер сериала слева и показывать следующие серии рядом с ним.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Список каналов в полноэкранном режиме",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Подведите мышь к левому краю или нажмите C в полноэкранном режиме, чтобы выдвинуть список каналов и переключить канал, не выходя из полноэкранного режима.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Список каналов и серий в полноэкранном режиме",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "В полноэкранном режиме наведите мышь на левый край, нажмите на него или нажмите C, чтобы показать список каналов прямого эфира или список серий сериала и переключиться, не выходя из полноэкранного режима.",
|
||||
"STREAM_FORMAT_DESCRIPTION": "Auto предпочитает HLS. При сбое в MPV, VLC или Embedded MPV остановите плеер, выберите ts, сохраните настройки и снова откройте канал. Веб-плееры могут один раз попробовать заявленный TS после первоначальной HTTP-ошибки HLS.",
|
||||
"LANGUAGE_DESCRIPTION": "Выбрать язык приложения",
|
||||
"THEME_DESCRIPTION": "Выбрать визуальную тему оформления",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Маленький",
|
||||
"COVER_SIZE_MEDIUM": "Средний",
|
||||
"COVER_SIZE_LARGE": "Большой",
|
||||
"COVER_TITLES": "Показывать названия под обложками",
|
||||
"COVER_TITLES_DESCRIPTION": "Отключите, чтобы в сетках фильмов и сериалов отображались только обложки: название появляется на обложке при наведении курсора или получении фокуса. Live-каналы, результаты поиска и ленты панели по-прежнему показывают подписи.",
|
||||
"EPG_VIEW_MODE": "Вид программы передач",
|
||||
"EPG_OFFSET_TITLE": "Смещение времени телепрограммы",
|
||||
"EPG_OFFSET_HINT": "Смещайте время отображения программ без обновления телепрограммы. Укажите минуты от -720 до 720.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Перезапустить приложение",
|
||||
"APP_UPDATE_OPEN_RELEASE": "Открыть релиз на GitHub",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Что нового",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "Заметки к версии {{version}} на GitHub не найдены. Релиз мог быть удалён или ещё не опубликован.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Не удалось загрузить заметки к версии.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Открыть страницу релизов",
|
||||
"APP_UPDATE_CHANNEL": "Канал обновлений",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable следует за выпущенными релизами. Nightly следует за каждым слиянием в master и автоматически устанавливает предварительные сборки. Применяется после сохранения.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Сохранить и проверить обновления {{channel}}",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Этот результат относится к каналу {{current}}. Сохраните, чтобы переключиться на {{next}} и проверить снова.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Это обновление найдено на канале {{current}}. Канал {{next}} ещё не проверялся.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly-сборки — непроверенные снимки master. Они могут работать нестабильно, а их изменения базы данных необратимы: после возврата на Stable эта сборка останется установленной, пока не выйдет более новый стабильный релиз. Сначала сделайте резервную копию плейлистов.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Вы используете nightly-сборку. Канал Stable предложит следующий стабильный релиз, как только он станет новее этой сборки.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: движок frame-copy (экспериментально)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Видео рисуется на канвасе приложения, поэтому меню и диалоги отображаются поверх него.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Вступит в силу после перезапуска приложения.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Выйти из полноэкранного режима",
|
||||
"CHANNEL_LIST": "Список каналов",
|
||||
"HIDE_CHANNEL_LIST": "Скрыть список каналов",
|
||||
"EPISODE_LIST": "Список серий",
|
||||
"HIDE_EPISODE_LIST": "Скрыть список серий",
|
||||
"SEARCH_CHANNELS": "Поиск каналов",
|
||||
"SEARCH_IN": "Поиск в {{title}}",
|
||||
"CLEAR_SEARCH": "Очистить поиск",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "программ",
|
||||
"LOADING": "Загрузка",
|
||||
"QUEUED": "В очереди",
|
||||
"IMPORT_PANEL_MINIMIZE": "Свернуть панель импорта EPG",
|
||||
"IMPORT_PANEL_EXPAND": "Развернуть панель импорта EPG",
|
||||
"IMPORT_SUMMARY_LOADING": "Загружается: {{count}}",
|
||||
"IMPORT_SUMMARY_QUEUED": "в очереди: {{count}}",
|
||||
"TOTAL_CHANNELS": "Всего каналов",
|
||||
"TOTAL_PROGRAMS": "Всего программ",
|
||||
"UP_TO_DATE": "EPG актуален, синхронизация не требуется",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Сериал отмечен как непросмотренный",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Не удалось обновить сериал",
|
||||
"SERIES_WATCH_MENU": "Действия просмотра сериала",
|
||||
"MOVIE_MARKED_WATCHED": "Отмечено как просмотренное",
|
||||
"MOVIE_MARKED_UNWATCHED": "Отмечено как непросмотренное",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "Не удалось обновить статус просмотра",
|
||||
"EPISODES_WATCHED": "Просмотрено {{count}} из {{total}} эпизодов",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Метаданные портала недоступны",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Об этом",
|
||||
"SEASONS_AND_EPISODES": "Сезоны и эпизоды",
|
||||
"SEASON_TAB": "Сезон {{number}}",
|
||||
"EPISODE_COUNT_ONE": "1 эпизод",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} эпизодов",
|
||||
"BACK_TO_PLAYING_EPISODE": "Вернуться к воспроизводимому эпизоду",
|
||||
"GRID_VIEW": "Вид сеткой",
|
||||
"LIST_VIEW": "Вид списком",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Этот контент недоступен на этом сервере",
|
||||
"PLAYBACK_ERROR": "Не удалось получить URL для воспроизведения",
|
||||
"LOADING_SEASONS": "Загрузка сезонов…",
|
||||
"LOADING_EPISODES": "Загрузка серий…",
|
||||
"EPISODES_LOAD_FAILED": "Не удалось загрузить серии.",
|
||||
"LOADING_ALL_CHANNELS": "Загрузка полного списка каналов…",
|
||||
"REFRESH_CHANNEL_LIST": "Обновить список каналов",
|
||||
"NO_CHANNELS_IN_CATEGORY": "В этой категории нет каналов",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Открыть недавно просмотренное",
|
||||
"SUBMIT_SHELL_SEARCH": "Отправить поиск в рабочей области",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Переключить полноэкранный режим окна приложения",
|
||||
"ZOOM_IN_OUT": "Увеличить или уменьшить масштаб приложения",
|
||||
"RESET_ZOOM": "Сбросить масштаб приложения",
|
||||
"TOGGLE_SIDEBAR": "Показать или скрыть боковую панель",
|
||||
"M3U_CHANNEL_NUMBER": "Выбрать канал M3U по номеру",
|
||||
"PLAY_PAUSE": "Запустить или приостановить воспроизведение во встроенном MPV",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "EPG'yi yenile",
|
||||
"REFRESH_ALL_EPG": "Tümünü yenile",
|
||||
"REMOVE_EPG_SOURCE": "EPG kaynağını kaldır",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "Güncel",
|
||||
"EPG_STATUS_STALE": "Yenilenmeli",
|
||||
"EPG_STATUS_ERROR": "Başarısız",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "EPG verileri temizlenemedi. Lütfen tekrar deneyin.",
|
||||
"CLEARING_EPG_DATA": "Temizleniyor…",
|
||||
"EPG_URL_ERROR": "Girilen değer geçerli bir URL değildir",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "Varsayılan video oynatıcıyı seçin",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Web oynatıcıları için birleşik kontroller",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "HTML5, Video.js ve ArtPlayer'da IPTVnator'ın ortak kontrollerini kullan.",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Oynatıcının çevresindeki boşluğu siyah çubuklar yerine bulanık ve karartılmış bir poster ile doldurur.",
|
||||
"PLAYER_UP_NEXT_RAIL": "Sıradaki bölümler paneli",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Geniş pencerelerde dizi oynatıcısını sola yaslar ve sıradaki bölümleri yanında gösterir.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Tam ekranda kanal listesi",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Tam ekrandayken fareyi sol kenara getirin veya C tuşuna basın: kanal listesi kayarak açılır ve tam ekrandan çıkmadan kanal değiştirebilirsiniz.",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "Tam ekranda kanal ve bölüm listesi",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "Tam ekrandayken fareyi sol kenara getirin, tıklayın veya C tuşuna basın; canlı yayının kanal listesi ya da dizinin bölüm listesi açılır ve tam ekrandan çıkmadan geçiş yapabilirsiniz.",
|
||||
"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",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "Küçük",
|
||||
"COVER_SIZE_MEDIUM": "Orta",
|
||||
"COVER_SIZE_LARGE": "Büyük",
|
||||
"COVER_TITLES": "Kapakların altında başlıkları göster",
|
||||
"COVER_TITLES_DESCRIPTION": "Film ve dizi ızgaralarında yalnızca poster gösteren bir görünüm için kapatın: üzerine geldiğinizde veya odaklandığınızda başlık kapağın üzerinde görünür. Canlı kanallar, arama sonuçları ve pano bölümleri etiketlerini korur.",
|
||||
"EPG_VIEW_MODE": "Rehber görünümü",
|
||||
"EPG_OFFSET_TITLE": "EPG zaman kayması",
|
||||
"EPG_OFFSET_HINT": "Rehberi yenilemeden görüntülenen program saatlerini kaydırın. -720 ile 720 arasında dakika kullanın.",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "Uygulamayı yeniden başlat",
|
||||
"APP_UPDATE_OPEN_RELEASE": "GitHub sürümünü aç",
|
||||
"APP_UPDATE_RELEASE_NOTES": "Yenilikler",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "GitHub'da {{version}} için sürüm notu bulunamadı. Sürüm kaldırılmış ya da henüz yayınlanmamış olabilir.",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "Sürüm notları yüklenemedi.",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "Sürümler sayfasını aç",
|
||||
"APP_UPDATE_CHANNEL": "Güncelleme kanalı",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable, yayımlanan sürümleri izler. Nightly, master’a yapılan her birleştirmeyi izler ve ön sürümleri otomatik olarak kurar. Kaydedildiğinde uygulanır.",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "Kaydet ve {{channel}} güncellemelerini denetle",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "Bu sonuç {{current}} kanalına aittir. {{next}} kanalına geçmek için kaydedin ve tekrar denetleyin.",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "Bu güncelleme {{current}} kanalında bulundu. {{next}} kanalı henüz denetlenmedi.",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly derlemeleri master’ın test edilmemiş anlık görüntüleridir. Bozuk olabilirler ve veritabanı değişiklikleri kalıcıdır: Stable’a geri dönüldüğünde bu derleme, daha yeni bir kararlı sürüm çıkana kadar kurulu kalır. Önce oynatma listelerinizi yedekleyin.",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "Bir nightly derlemesi kullanıyorsunuz. Stable kanalı, bu derlemeden daha yeni olduğunda sonraki kararlı sürümü sunacaktır.",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: frame-copy motoru (deneysel)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Video uygulama tuvaline çizilir, böylece menüler ve iletişim kutuları üzerinde görüntülenir.",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Uygulama yeniden başlatıldıktan sonra geçerli olur.",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "Tam ekrandan çık",
|
||||
"CHANNEL_LIST": "Kanal listesi",
|
||||
"HIDE_CHANNEL_LIST": "Kanal listesini gizle",
|
||||
"EPISODE_LIST": "Bölüm listesi",
|
||||
"HIDE_EPISODE_LIST": "Bölüm listesini gizle",
|
||||
"SEARCH_CHANNELS": "Kanal ara",
|
||||
"SEARCH_IN": "{{title}} içinde ara",
|
||||
"CLEAR_SEARCH": "Aramayı temizle",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "programlar",
|
||||
"LOADING": "Yükleniyor",
|
||||
"QUEUED": "Sırada",
|
||||
"IMPORT_PANEL_MINIMIZE": "EPG içe aktarma panelini küçült",
|
||||
"IMPORT_PANEL_EXPAND": "EPG içe aktarma panelini genişlet",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} yükleniyor",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} sırada",
|
||||
"TOTAL_CHANNELS": "Toplam kanal",
|
||||
"TOTAL_PROGRAMS": "Toplam program",
|
||||
"UP_TO_DATE": "EPG güncel, senkronizasyona gerek yok",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "Dizi izlenmedi olarak işaretlendi",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "Dizi güncellemesi başarısız oldu",
|
||||
"SERIES_WATCH_MENU": "Dizi izleme işlemleri",
|
||||
"MOVIE_MARKED_WATCHED": "İzlendi olarak işaretlendi",
|
||||
"MOVIE_MARKED_UNWATCHED": "İzlenmedi olarak işaretlendi",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "İzlenme durumu güncellemesi başarısız oldu",
|
||||
"EPISODES_WATCHED": "{{total}} bölümden {{count}} tanesi izlendi",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "Portal üst verisi mevcut değil",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "Hakkında",
|
||||
"SEASONS_AND_EPISODES": "Sezonlar ve Bölümler",
|
||||
"SEASON_TAB": "{{number}}. Sezon",
|
||||
"EPISODE_COUNT_ONE": "1 bölüm",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} bölüm",
|
||||
"BACK_TO_PLAYING_EPISODE": "Oynatılan bölüme dön",
|
||||
"GRID_VIEW": "Izgara görünümü",
|
||||
"LIST_VIEW": "Liste görünümü",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "Bu içerik bu sunucuda mevcut değil",
|
||||
"PLAYBACK_ERROR": "Oynatma URL'si alınamadı",
|
||||
"LOADING_SEASONS": "Sezonlar yükleniyor...",
|
||||
"LOADING_EPISODES": "Bölümler yükleniyor...",
|
||||
"EPISODES_LOAD_FAILED": "Bölümler yüklenemedi.",
|
||||
"LOADING_ALL_CHANNELS": "Tüm kanal listesi yükleniyor…",
|
||||
"REFRESH_CHANNEL_LIST": "Kanal listesini yenile",
|
||||
"NO_CHANNELS_IN_CATEGORY": "Bu kategoride kanal yok",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "Son görüntülenenleri aç",
|
||||
"SUBMIT_SHELL_SEARCH": "Çalışma alanı aramasını gönder",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "Uygulama penceresi tam ekranını aç/kapat",
|
||||
"ZOOM_IN_OUT": "Uygulamayı yakınlaştır veya uzaklaştır",
|
||||
"RESET_ZOOM": "Uygulama yakınlaştırmasını sıfırla",
|
||||
"TOGGLE_SIDEBAR": "Kenar çubuğunu aç/kapat",
|
||||
"M3U_CHANNEL_NUMBER": "M3U kanalını numarayla seç",
|
||||
"PLAY_PAUSE": "Gömülü MPV oynatmasını oynat veya duraklat",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "刷新电子节目单EPG",
|
||||
"REFRESH_ALL_EPG": "全部刷新",
|
||||
"REMOVE_EPG_SOURCE": "移除 EPG 源",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "已更新",
|
||||
"EPG_STATUS_STALE": "需要刷新",
|
||||
"EPG_STATUS_ERROR": "失败",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "清除 EPG 数据失败。请重试。",
|
||||
"CLEARING_EPG_DATA": "清除中…",
|
||||
"EPG_URL_ERROR": "该值不是有效的 URL",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "选择默认视频播放器",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Web 播放器统一控件",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "在 HTML5、Video.js 和 ArtPlayer 中使用 IPTVnator 的共享控件。",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "用模糊、暗淡的海报填充播放器周围的空白,而不是黑条纹。",
|
||||
"PLAYER_UP_NEXT_RAIL": "「接下来播放」剧集栏",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "在宽窗口下,将剧集播放器停靠在左侧,并在旁边显示后续剧集。",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "全屏时显示频道列表",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "全屏播放时,将鼠标移到左边缘或按 C 键即可滑出频道列表,无需退出全屏即可切换频道。",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "全屏时的频道和剧集列表",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "全屏时将鼠标停在左边缘、点击它或按 C 键,即可滑出直播的频道列表或剧集的分集列表,无需退出全屏即可切换。",
|
||||
"STREAM_FORMAT_DESCRIPTION": "选择默认的流格式(xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "选择应用程序的语言",
|
||||
"THEME_DESCRIPTION": "选择应用程序的视觉主题",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "小",
|
||||
"COVER_SIZE_MEDIUM": "中",
|
||||
"COVER_SIZE_LARGE": "大",
|
||||
"COVER_TITLES": "在封面下方显示标题",
|
||||
"COVER_TITLES_DESCRIPTION": "关闭后,电影和剧集网格将呈现纯海报墙:将指针悬停在封面上或将其聚焦时才会显示标题。直播频道、搜索结果和仪表盘栏目将始终显示标签。",
|
||||
"EPG_VIEW_MODE": "节目指南视图",
|
||||
"EPG_OFFSET_TITLE": "EPG 时间偏移",
|
||||
"EPG_OFFSET_HINT": "无需刷新节目指南即可调整显示的节目时间。请输入 -720 到 720 之间的分钟数。",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "重启应用",
|
||||
"APP_UPDATE_OPEN_RELEASE": "打开 GitHub 发布页",
|
||||
"APP_UPDATE_RELEASE_NOTES": "更新内容",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "在 GitHub 上未找到 {{version}} 的更新说明。该版本可能已被移除或尚未发布。",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "无法加载更新说明。",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "打开版本发布页面",
|
||||
"APP_UPDATE_CHANNEL": "更新频道",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable 跟随正式发布的版本。Nightly 跟随每次合并到 master 的更改,并自动安装预发布构建。保存后生效。",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "保存并检查 {{channel}} 更新",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "此结果针对 {{current}} 频道。保存以切换到 {{next}} 并再次检查。",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "此更新来自 {{current}} 频道。{{next}} 频道尚未检查。",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly 构建是 master 的未经测试快照。它们可能出错,且其数据库更改不可撤销:切换回 Stable 后,此构建会一直保留,直到出现更新的稳定版本。请先备份您的播放列表。",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "您正在使用 nightly 构建。Stable 频道会在下一个稳定版本比此构建更新时提供该版本。",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "嵌入式 MPV:帧拷贝引擎(实验性)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "视频渲染到应用画布上,菜单和对话框可以显示在其上方。",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "重启应用后生效。",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "退出全屏",
|
||||
"CHANNEL_LIST": "频道列表",
|
||||
"HIDE_CHANNEL_LIST": "隐藏频道列表",
|
||||
"EPISODE_LIST": "剧集列表",
|
||||
"HIDE_EPISODE_LIST": "隐藏剧集列表",
|
||||
"SEARCH_CHANNELS": "搜索频道",
|
||||
"SEARCH_IN": "在 {{title}} 中搜索",
|
||||
"CLEAR_SEARCH": "清除搜索",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "节目",
|
||||
"LOADING": "加载中",
|
||||
"QUEUED": "排队中",
|
||||
"IMPORT_PANEL_MINIMIZE": "最小化 EPG 导入面板",
|
||||
"IMPORT_PANEL_EXPAND": "展开 EPG 导入面板",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} 个加载中",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} 个排队中",
|
||||
"TOTAL_CHANNELS": "频道总数",
|
||||
"TOTAL_PROGRAMS": "节目总数",
|
||||
"UP_TO_DATE": "EPG 已为最新,无需同步",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "剧集已标记为未观看",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "无法更新剧集",
|
||||
"SERIES_WATCH_MENU": "剧集观看操作",
|
||||
"MOVIE_MARKED_WATCHED": "已标记为已观看",
|
||||
"MOVIE_MARKED_UNWATCHED": "已标记为未观看",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "无法更新观看状态",
|
||||
"EPISODES_WATCHED": "已观看 {{count}} / {{total}} 集",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "门户元数据不可用",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "简介",
|
||||
"SEASONS_AND_EPISODES": "季与集",
|
||||
"SEASON_TAB": "第 {{number}} 季",
|
||||
"EPISODE_COUNT_ONE": "1 集",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} 集",
|
||||
"BACK_TO_PLAYING_EPISODE": "返回正在播放的剧集",
|
||||
"GRID_VIEW": "网格视图",
|
||||
"LIST_VIEW": "列表视图",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "此服务器上没有此内容",
|
||||
"PLAYBACK_ERROR": "获取播放 URL 失败",
|
||||
"LOADING_SEASONS": "正在加载季…",
|
||||
"LOADING_EPISODES": "正在加载剧集…",
|
||||
"EPISODES_LOAD_FAILED": "无法加载剧集。",
|
||||
"LOADING_ALL_CHANNELS": "正在加载完整频道列表…",
|
||||
"REFRESH_CHANNEL_LIST": "刷新频道列表",
|
||||
"NO_CHANNELS_IN_CATEGORY": "此分类中没有频道",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "打开最近观看",
|
||||
"SUBMIT_SHELL_SEARCH": "提交工作区搜索",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "切换应用窗口全屏",
|
||||
"ZOOM_IN_OUT": "放大或缩小应用",
|
||||
"RESET_ZOOM": "重置应用缩放",
|
||||
"TOGGLE_SIDEBAR": "切换侧边栏",
|
||||
"M3U_CHANNEL_NUMBER": "按数字选择 M3U 频道",
|
||||
"PLAY_PAUSE": "播放或暂停嵌入式 MPV 播放",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"REFRESH_EPG": "重新整理 EPG",
|
||||
"REFRESH_ALL_EPG": "全部重新整理",
|
||||
"REMOVE_EPG_SOURCE": "移除 EPG 來源",
|
||||
"BROWSE_EPG_FILE": "Choose a local XMLTV file…",
|
||||
"EPG_STATUS_FRESH": "已更新",
|
||||
"EPG_STATUS_STALE": "需要重新整理",
|
||||
"EPG_STATUS_ERROR": "失敗",
|
||||
@@ -364,6 +365,9 @@
|
||||
"EPG_DATA_CLEAR_FAILED": "清除 EPG 資料失敗。請重試。",
|
||||
"CLEARING_EPG_DATA": "正在清除…",
|
||||
"EPG_URL_ERROR": "該值不是有效的網址",
|
||||
"EPG_SOURCE_LIST_TITLE": "Source list",
|
||||
"EPG_SOURCE_FORMATS_HINT": "Remote XMLTV links (http/https) and files on this computer are both supported, as plain .xml or gzip-compressed .xml.gz. Local files need an absolute path or a file:// URL.",
|
||||
"EPG_SOURCE_FORMATS_EXAMPLES": "Examples:",
|
||||
"VIDEO_PLAYER_DESCRIPTION": "選擇預設影片播放器",
|
||||
"WEB_PLAYER_SHARED_CONTROLS": "Web 播放器統一控制項",
|
||||
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "在 HTML5、Video.js 和 ArtPlayer 中使用 IPTVnator 的共用控制項。",
|
||||
@@ -371,8 +375,8 @@
|
||||
"PLAYER_AMBIENT_MODE_DESCRIPTION": "用模糊、昏暗的海報填充播放器周圍的空間,取代純黑色邊框。",
|
||||
"PLAYER_UP_NEXT_RAIL": "即將播放集數側欄",
|
||||
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "在較寬的視窗中,將影集播放器停靠在左側,並在旁邊顯示即將播放的集數。",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "全螢幕時顯示頻道清單",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "全螢幕播放時,將滑鼠移到左邊緣或按 C 鍵即可滑出頻道清單,不必離開全螢幕就能切換頻道。",
|
||||
"FULLSCREEN_CHANNEL_PANEL": "全螢幕時的頻道與集數清單",
|
||||
"FULLSCREEN_CHANNEL_PANEL_DESCRIPTION": "全螢幕時將滑鼠停在左側邊緣、點擊它或按 C 鍵,即可滑出直播的頻道清單或影集的集數清單,不需離開全螢幕即可切換。",
|
||||
"STREAM_FORMAT_DESCRIPTION": "選擇預設串流格式(xtream)",
|
||||
"LANGUAGE_DESCRIPTION": "選擇應用程式語言",
|
||||
"THEME_DESCRIPTION": "選擇應用程式視覺主題",
|
||||
@@ -381,6 +385,8 @@
|
||||
"COVER_SIZE_SMALL": "小",
|
||||
"COVER_SIZE_MEDIUM": "中",
|
||||
"COVER_SIZE_LARGE": "大",
|
||||
"COVER_TITLES": "在封面下方顯示標題",
|
||||
"COVER_TITLES_DESCRIPTION": "停用後,電影和影集網格會變成純海報牆:滑鼠指向或聚焦封面時才會顯示標題。直播頻道、搜尋結果和儀表板欄位仍會保留標籤。",
|
||||
"EPG_VIEW_MODE": "節目表檢視方式",
|
||||
"EPG_OFFSET_TITLE": "EPG 時間偏移",
|
||||
"EPG_OFFSET_HINT": "無需重新整理節目表即可調整顯示的節目時間。請輸入 -720 到 720 之間的分鐘數。",
|
||||
@@ -519,6 +525,18 @@
|
||||
"APP_UPDATE_INSTALL": "重新啟動應用程式",
|
||||
"APP_UPDATE_OPEN_RELEASE": "開啟 GitHub 發布頁",
|
||||
"APP_UPDATE_RELEASE_NOTES": "更新內容",
|
||||
"APP_UPDATE_RELEASE_NOTES_NOT_FOUND": "在 GitHub 上找不到 {{version}} 的更新說明。該版本可能已被移除或尚未發布。",
|
||||
"APP_UPDATE_RELEASE_NOTES_LOAD_FAILED": "無法載入更新說明。",
|
||||
"APP_UPDATE_OPEN_RELEASES_PAGE": "開啟版本發布頁面",
|
||||
"APP_UPDATE_CHANNEL": "更新頻道",
|
||||
"APP_UPDATE_CHANNEL_DESCRIPTION": "Stable 跟隨正式發布的版本。Nightly 跟隨每次合併到 master 的變更,並自動安裝預先發布的建置版本。儲存後生效。",
|
||||
"APP_UPDATE_CHANNEL_STABLE": "Stable",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY": "Nightly",
|
||||
"APP_UPDATE_SAVE_AND_CHECK": "儲存並檢查 {{channel}} 更新",
|
||||
"APP_UPDATE_CHANNEL_PENDING_HINT": "此結果針對 {{current}} 頻道。儲存以切換至 {{next}} 並再次檢查。",
|
||||
"APP_UPDATE_CHANNEL_RETAINED_HINT": "此更新來自 {{current}} 頻道。{{next}} 頻道尚未檢查。",
|
||||
"APP_UPDATE_CHANNEL_NIGHTLY_WARNING": "Nightly 建置是 master 未經測試的快照。它們可能出錯,且其資料庫變更無法復原:切換回 Stable 後,此建置會一直保留,直到出現更新的穩定版本。請先備份您的播放清單。",
|
||||
"APP_UPDATE_NIGHTLY_ON_STABLE_HINT": "您正在使用 nightly 建置。Stable 頻道會在下一個穩定版本比此建置更新時提供該版本。",
|
||||
"EMBEDDED_MPV_FRAME_COPY": "內嵌 MPV:幀複製引擎(實驗性)",
|
||||
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "影片會繪製到應用程式畫布上,選單和對話框可顯示在其上方。",
|
||||
"EMBEDDED_MPV_FRAME_COPY_RESTART": "重新啟動應用程式後生效。",
|
||||
@@ -622,6 +640,8 @@
|
||||
"EXIT_FULLSCREEN": "離開全螢幕",
|
||||
"CHANNEL_LIST": "頻道清單",
|
||||
"HIDE_CHANNEL_LIST": "隱藏頻道清單",
|
||||
"EPISODE_LIST": "集數清單",
|
||||
"HIDE_EPISODE_LIST": "隱藏集數清單",
|
||||
"SEARCH_CHANNELS": "搜尋頻道",
|
||||
"SEARCH_IN": "在 {{title}} 中搜尋",
|
||||
"CLEAR_SEARCH": "清除搜尋",
|
||||
@@ -882,6 +902,10 @@
|
||||
"PROGRAMS": "節目",
|
||||
"LOADING": "載入中",
|
||||
"QUEUED": "佇列中",
|
||||
"IMPORT_PANEL_MINIMIZE": "最小化 EPG 匯入面板",
|
||||
"IMPORT_PANEL_EXPAND": "展開 EPG 匯入面板",
|
||||
"IMPORT_SUMMARY_LOADING": "{{count}} 個載入中",
|
||||
"IMPORT_SUMMARY_QUEUED": "{{count}} 個排隊中",
|
||||
"TOTAL_CHANNELS": "頻道總數",
|
||||
"TOTAL_PROGRAMS": "節目總數",
|
||||
"UP_TO_DATE": "EPG 已是最新,無需同步",
|
||||
@@ -1094,6 +1118,9 @@
|
||||
"SERIES_MARKED_UNWATCHED": "本影集已標記為未觀看",
|
||||
"SERIES_WATCH_UPDATE_FAILED": "本影集更新失敗",
|
||||
"SERIES_WATCH_MENU": "影集觀看操作",
|
||||
"MOVIE_MARKED_WATCHED": "已標記為已觀看",
|
||||
"MOVIE_MARKED_UNWATCHED": "已標記為未觀看",
|
||||
"MOVIE_WATCH_UPDATE_FAILED": "觀看狀態更新失敗",
|
||||
"EPISODES_WATCHED": "已觀看 {{total}} 集中的 {{count}} 集",
|
||||
"DETAIL_FALLBACK": {
|
||||
"STATUS": "無法取得入口網站中繼資料",
|
||||
@@ -1247,6 +1274,8 @@
|
||||
"ABOUT": "簡介",
|
||||
"SEASONS_AND_EPISODES": "季數與集數",
|
||||
"SEASON_TAB": "第 {{number}} 季",
|
||||
"EPISODE_COUNT_ONE": "1 集",
|
||||
"EPISODE_COUNT_OTHER": "{{count}} 集",
|
||||
"BACK_TO_PLAYING_EPISODE": "返回正在播放的集數",
|
||||
"GRID_VIEW": "格狀檢視",
|
||||
"LIST_VIEW": "清單檢視",
|
||||
@@ -1260,6 +1289,8 @@
|
||||
"CONTENT_NOT_AVAILABLE": "此伺服器上未提供此內容",
|
||||
"PLAYBACK_ERROR": "無法取得播放網址",
|
||||
"LOADING_SEASONS": "正在載入季數...",
|
||||
"LOADING_EPISODES": "正在載入集數...",
|
||||
"EPISODES_LOAD_FAILED": "無法載入集數。",
|
||||
"LOADING_ALL_CHANNELS": "正在載入完整頻道清單…",
|
||||
"REFRESH_CHANNEL_LIST": "重新整理頻道清單",
|
||||
"NO_CHANNELS_IN_CATEGORY": "此分類中沒有頻道",
|
||||
@@ -1751,6 +1782,8 @@
|
||||
"OPEN_RECENTLY_VIEWED": "開啟最近觀看",
|
||||
"SUBMIT_SHELL_SEARCH": "提交工作區搜尋",
|
||||
"TOGGLE_WINDOW_FULLSCREEN": "切換應用程式視窗全螢幕",
|
||||
"ZOOM_IN_OUT": "放大或縮小應用程式",
|
||||
"RESET_ZOOM": "重設應用程式縮放",
|
||||
"TOGGLE_SIDEBAR": "切換側邊欄",
|
||||
"M3U_CHANNEL_NUMBER": "按數字選擇 M3U 頻道",
|
||||
"PLAY_PAUSE": "播放或暫停嵌入式 MPV 播放",
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { resolveRestoredRendererRoute } from '@iptvnator/shared/interfaces';
|
||||
import { registerAppDateLocales } from './app/app-date-locales';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { appConfig } from './app/app.config';
|
||||
|
||||
registerAppDateLocales();
|
||||
|
||||
// A reloaded packaged renderer arrives on index.html with the route it was
|
||||
// on carried in the query string (the Electron main process recovers the
|
||||
// file:// reload that way). Put that route back before the router reads the
|
||||
// URL for its initial navigation.
|
||||
const restoredHref = resolveRestoredRendererRoute(
|
||||
window.location.href,
|
||||
document.baseURI
|
||||
);
|
||||
if (restoredHref !== null) {
|
||||
window.history.replaceState(window.history.state, '', restoredHref);
|
||||
}
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
.then(() => {
|
||||
// Splash is rendered eagerly by index.html so the user sees something
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone/index.mjs';
|
||||
// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler.
|
||||
import 'zone.js';
|
||||
import 'zone.js/testing';
|
||||
import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless/index.mjs';
|
||||
import { installDuplicateVideoJsQualityLevelsWarnFilter } from '@iptvnator/shared/testing';
|
||||
|
||||
Object.defineProperty(globalThis, 'jest', {
|
||||
@@ -25,7 +28,7 @@ if (!Element.prototype.animate) {
|
||||
|
||||
installDuplicateVideoJsQualityLevelsWarnFilter();
|
||||
|
||||
setupZoneTestEnv({
|
||||
setupZonelessTestEnv({
|
||||
errorOnUnknownElements: true,
|
||||
errorOnUnknownProperties: true,
|
||||
});
|
||||
Reference in new issue
Block a user