mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 13:46:16 -08:00
feat(stalker): discover portal connection on edit
This commit is contained in:
1 parent
c95f826739
commit
d2d91e322b
47 files changed
+2335
-497
No files matched your search
@@ -27,6 +27,7 @@ import {
|
||||
PORTAL_PLAYER,
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { PLAYLIST_PLAYER_ACTIONS } from '@iptvnator/playlist/shared/util';
|
||||
import { STALKER_PLAYLIST_CONNECTION_EDITOR } from '@iptvnator/playlist/shared/ui';
|
||||
import { provideXtreamDataSource } from '@iptvnator/portal/xtream/data-access';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import { dbConfig } from '@iptvnator/shared/interfaces';
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
import { providePortalPlaybackPositions } from './services/portal-playback-positions.service';
|
||||
import { PwaService } from './services/pwa.service';
|
||||
import { shouldEnableServiceWorker } from './services/runtime-config';
|
||||
import { AppStalkerPlaylistConnectionEditorService } from './services/stalker-playlist-connection-editor.service';
|
||||
import { provideWorkspaceShellActions } from './services/workspace-shell-actions.service';
|
||||
|
||||
// AoT requires an exported function for factories
|
||||
@@ -152,6 +154,10 @@ export const appConfig: ApplicationConfig = {
|
||||
provide: PLAYLIST_PLAYER_ACTIONS,
|
||||
useExisting: AppPortalNavigationActionsService,
|
||||
},
|
||||
{
|
||||
provide: STALKER_PLAYLIST_CONNECTION_EDITOR,
|
||||
useExisting: AppStalkerPlaylistConnectionEditorService,
|
||||
},
|
||||
...provideWorkspaceShellActions(),
|
||||
...provideXtreamDataSource(),
|
||||
{
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
StalkerPortalDiscoveryService,
|
||||
StalkerPortalError,
|
||||
StalkerPortalRepairService,
|
||||
StalkerSessionService,
|
||||
StalkerStore,
|
||||
stalkerSessionFingerprint,
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import { STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS } from '@iptvnator/playlist/shared/ui';
|
||||
import type { PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
import { AppStalkerPlaylistConnectionEditorService } from './stalker-playlist-connection-editor.service';
|
||||
|
||||
describe('AppStalkerPlaylistConnectionEditorService', () => {
|
||||
const discovery = {
|
||||
discover: jest.fn(),
|
||||
};
|
||||
const portalRepair = {
|
||||
applyOverride: jest.fn((playlist: PlaylistMeta) => playlist),
|
||||
retireForPlaylistEdit: jest.fn(),
|
||||
};
|
||||
const stalkerSession = {
|
||||
replaceSessionAfterEdit: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
let activePlaylist: PlaylistMeta | undefined;
|
||||
const stalkerStore = {
|
||||
currentPlaylist: jest.fn(() => activePlaylist),
|
||||
setCurrentPlaylist: jest.fn((playlist: PlaylistMeta) => {
|
||||
activePlaylist = playlist;
|
||||
}),
|
||||
};
|
||||
const translate = {
|
||||
instant: jest.fn((key: string) => key),
|
||||
};
|
||||
|
||||
const draft: PlaylistMeta = {
|
||||
_id: 'stalker-1',
|
||||
title: 'Stalker Portal',
|
||||
count: 0,
|
||||
importDate: '2026-08-08T00:00:00.000Z',
|
||||
portalUrl: 'https://portal.example.com/c',
|
||||
macAddress: '00:1A:79:AA:BB:CC',
|
||||
username: 'subscriber',
|
||||
password: 'secret',
|
||||
stalkerSerialNumber: ' SERIAL ',
|
||||
stalkerDeviceId1: ' DEVICE-1 ',
|
||||
stalkerDeviceId2: '',
|
||||
stalkerSignature1: ' SIGNATURE-1 ',
|
||||
stalkerSignature2: '',
|
||||
};
|
||||
|
||||
let service: AppStalkerPlaylistConnectionEditorService;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
activePlaylist = undefined;
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
AppStalkerPlaylistConnectionEditorService,
|
||||
{
|
||||
provide: StalkerPortalDiscoveryService,
|
||||
useValue: discovery,
|
||||
},
|
||||
{
|
||||
provide: StalkerPortalRepairService,
|
||||
useValue: portalRepair,
|
||||
},
|
||||
{
|
||||
provide: StalkerSessionService,
|
||||
useValue: stalkerSession,
|
||||
},
|
||||
{ provide: StalkerStore, useValue: stalkerStore },
|
||||
{ provide: TranslateService, useValue: translate },
|
||||
],
|
||||
});
|
||||
service = TestBed.inject(AppStalkerPlaylistConnectionEditorService);
|
||||
});
|
||||
|
||||
it('resolves a simple portal and clears the previous full session', async () => {
|
||||
discovery.discover.mockResolvedValue({
|
||||
status: 'resolved',
|
||||
portalUrl: 'https://portal.example.com/portal.php',
|
||||
isFullStalkerPortal: false,
|
||||
});
|
||||
|
||||
const result = await service.resolveConnection(draft);
|
||||
|
||||
expect(discovery.discover).toHaveBeenCalledWith(
|
||||
draft.portalUrl,
|
||||
draft.macAddress,
|
||||
{
|
||||
serialNumber: 'SERIAL',
|
||||
deviceId1: 'DEVICE-1',
|
||||
signature1: 'SIGNATURE-1',
|
||||
},
|
||||
{
|
||||
credentials: {
|
||||
username: 'subscriber',
|
||||
password: 'secret',
|
||||
},
|
||||
}
|
||||
);
|
||||
expect(result).toEqual({
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.RESOLVED,
|
||||
playlist: {
|
||||
...draft,
|
||||
portalUrl: 'https://portal.example.com/portal.php',
|
||||
isFullStalkerPortal: false,
|
||||
stalkerSerialNumber: 'SERIAL',
|
||||
stalkerDeviceId1: 'DEVICE-1',
|
||||
stalkerDeviceId2: '',
|
||||
stalkerSignature1: 'SIGNATURE-1',
|
||||
stalkerSignature2: '',
|
||||
stalkerSessionPatch: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('replaces a full-portal session with the confirmed authorization result', async () => {
|
||||
discovery.discover.mockResolvedValue({
|
||||
status: 'resolved',
|
||||
portalUrl: 'https://portal.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
token: 'NEW_TOKEN',
|
||||
watchdogTimeoutSeconds: 75,
|
||||
timeslotSeconds: 8,
|
||||
accountInfo: {
|
||||
login: 'subscriber',
|
||||
expire_date: 1800000000,
|
||||
tariff_plan_name: 'Premium',
|
||||
status: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await service.resolveConnection(draft);
|
||||
const resolvedPlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://portal.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
stalkerSerialNumber: 'SERIAL',
|
||||
stalkerDeviceId1: 'DEVICE-1',
|
||||
stalkerDeviceId2: '',
|
||||
stalkerSignature1: 'SIGNATURE-1',
|
||||
stalkerSignature2: '',
|
||||
};
|
||||
|
||||
expect(result).toEqual({
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.RESOLVED,
|
||||
playlist: {
|
||||
...resolvedPlaylist,
|
||||
stalkerSessionPatch: {
|
||||
stalkerToken: 'NEW_TOKEN',
|
||||
stalkerSessionIdentity:
|
||||
stalkerSessionFingerprint(resolvedPlaylist),
|
||||
stalkerWatchdogTimeout: 75,
|
||||
stalkerTimeslot: 8,
|
||||
stalkerAccountInfo: {
|
||||
login: 'subscriber',
|
||||
expireDate: 1800000000,
|
||||
tariffPlanName: 'Premium',
|
||||
status: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a user-facing portal refusal without producing an update', async () => {
|
||||
discovery.discover.mockResolvedValue({
|
||||
status: 'auth-rejected',
|
||||
portalUrl: 'https://portal.example.com/server/load.php',
|
||||
error: new StalkerPortalError(
|
||||
'login-rejected',
|
||||
'Subscription expired'
|
||||
),
|
||||
});
|
||||
|
||||
await expect(service.resolveConnection(draft)).resolves.toEqual({
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.AUTH_REJECTED,
|
||||
message:
|
||||
'HOME.STALKER_PORTAL.LOGIN_REJECTED HOME.STALKER_PORTAL.PORTAL_MESSAGE',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a dedicated error when no endpoint can be reached', async () => {
|
||||
discovery.discover.mockResolvedValue({ status: 'unreachable' });
|
||||
|
||||
await expect(service.resolveConnection(draft)).resolves.toEqual({
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.UNREACHABLE,
|
||||
message: 'HOME.STALKER_PORTAL.EDIT_UNREACHABLE',
|
||||
});
|
||||
});
|
||||
|
||||
it('replaces the active runtime playlist and session after a resolved full-portal edit', async () => {
|
||||
activePlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://old.example.com/portal.php',
|
||||
isFullStalkerPortal: false,
|
||||
};
|
||||
const resolvedPlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://new.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
stalkerSessionPatch: {
|
||||
stalkerToken: 'NEW_TOKEN',
|
||||
stalkerSessionIdentity: 'new-fingerprint',
|
||||
stalkerWatchdogTimeout: 90,
|
||||
stalkerTimeslot: 5,
|
||||
},
|
||||
};
|
||||
|
||||
await service.applyResolvedConnection(resolvedPlaylist);
|
||||
|
||||
expect(portalRepair.retireForPlaylistEdit).toHaveBeenCalledWith(
|
||||
draft._id
|
||||
);
|
||||
expect(stalkerSession.replaceSessionAfterEdit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
portalUrl: 'https://new.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
stalkerToken: 'NEW_TOKEN',
|
||||
stalkerSessionIdentity: 'new-fingerprint',
|
||||
stalkerWatchdogTimeout: 90,
|
||||
stalkerTimeslot: 5,
|
||||
})
|
||||
);
|
||||
expect(stalkerStore.setCurrentPlaylist).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
portalUrl: 'https://new.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
})
|
||||
);
|
||||
expect(activePlaylist?.portalUrl).toBe(
|
||||
'https://new.example.com/server/load.php'
|
||||
);
|
||||
});
|
||||
|
||||
it('clears the session and stops full-portal runtime behavior after a resolved simple edit', async () => {
|
||||
activePlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://old.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
};
|
||||
const resolvedPlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://new.example.com/portal.php',
|
||||
isFullStalkerPortal: false,
|
||||
stalkerSessionPatch: null,
|
||||
};
|
||||
|
||||
await service.applyResolvedConnection(resolvedPlaylist);
|
||||
|
||||
expect(portalRepair.retireForPlaylistEdit).toHaveBeenCalledWith(
|
||||
draft._id
|
||||
);
|
||||
expect(stalkerSession.replaceSessionAfterEdit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
portalUrl: 'https://new.example.com/portal.php',
|
||||
isFullStalkerPortal: false,
|
||||
stalkerToken: undefined,
|
||||
stalkerSessionIdentity: undefined,
|
||||
})
|
||||
);
|
||||
expect(activePlaylist).toEqual(
|
||||
expect.objectContaining({
|
||||
portalUrl: 'https://new.example.com/portal.php',
|
||||
isFullStalkerPortal: false,
|
||||
stalkerToken: undefined,
|
||||
stalkerSessionIdentity: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('repoints the active snapshot before an old authentication finishes draining', async () => {
|
||||
activePlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://old.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
};
|
||||
let finishReplacement: () => void = () => undefined;
|
||||
stalkerSession.replaceSessionAfterEdit.mockReturnValueOnce(
|
||||
new Promise<void>((resolve) => {
|
||||
finishReplacement = resolve;
|
||||
})
|
||||
);
|
||||
const resolvedPlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://new.example.com/portal.php',
|
||||
isFullStalkerPortal: false,
|
||||
stalkerSessionPatch: null,
|
||||
};
|
||||
|
||||
const applying = service.applyResolvedConnection(resolvedPlaylist);
|
||||
|
||||
expect(activePlaylist?.portalUrl).toBe(
|
||||
'https://new.example.com/portal.php'
|
||||
);
|
||||
let settled = false;
|
||||
void applying.then(() => (settled = true));
|
||||
await Promise.resolve();
|
||||
expect(settled).toBe(false);
|
||||
|
||||
finishReplacement();
|
||||
await applying;
|
||||
});
|
||||
|
||||
it.each([true, false])(
|
||||
'keeps the exact credential-only Edit result when a prior endpoint repair exists (active=%s)',
|
||||
async (isActive) => {
|
||||
const resolvedPlaylist = {
|
||||
...draft,
|
||||
portalUrl: 'https://portal.example.com/server/load.php',
|
||||
isFullStalkerPortal: true,
|
||||
username: 'new-user',
|
||||
stalkerSessionPatch: {
|
||||
stalkerToken: 'NEW_TOKEN',
|
||||
stalkerSessionIdentity: 'new-fingerprint',
|
||||
stalkerWatchdogTimeout: 90,
|
||||
stalkerTimeslot: 5,
|
||||
},
|
||||
};
|
||||
// This is the remembered A→B repair that used to rewrite the
|
||||
// newly proven A endpoint when only credentials changed.
|
||||
portalRepair.applyOverride.mockReturnValue({
|
||||
...resolvedPlaylist,
|
||||
portalUrl: 'https://portal.example.com/portal.php',
|
||||
});
|
||||
activePlaylist = isActive
|
||||
? {
|
||||
...draft,
|
||||
portalUrl: 'https://portal.example.com/portal.php',
|
||||
}
|
||||
: undefined;
|
||||
|
||||
await service.applyResolvedConnection(resolvedPlaylist);
|
||||
|
||||
expect(portalRepair.applyOverride).not.toHaveBeenCalled();
|
||||
expect(portalRepair.retireForPlaylistEdit).toHaveBeenCalledWith(
|
||||
draft._id
|
||||
);
|
||||
expect(stalkerSession.replaceSessionAfterEdit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
portalUrl: 'https://portal.example.com/server/load.php',
|
||||
username: 'new-user',
|
||||
stalkerToken: 'NEW_TOKEN',
|
||||
})
|
||||
);
|
||||
expect(stalkerStore.setCurrentPlaylist).toHaveBeenCalledTimes(
|
||||
isActive ? 1 : 0
|
||||
);
|
||||
if (isActive) {
|
||||
expect(activePlaylist?.portalUrl).toBe(
|
||||
'https://portal.example.com/server/load.php'
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,199 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
asStalkerPortalError,
|
||||
StalkerPortalDiscoveryService,
|
||||
StalkerPortalRepairService,
|
||||
StalkerSessionService,
|
||||
StalkerStore,
|
||||
STALKER_WATCHDOG_DEFAULT_PERIOD_SECONDS,
|
||||
stalkerSessionFingerprint,
|
||||
type StalkerPortalErrorKind,
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS,
|
||||
type StalkerPlaylistConnectionEditor,
|
||||
type StalkerPlaylistConnectionResult,
|
||||
} from '@iptvnator/playlist/shared/ui';
|
||||
import {
|
||||
normalizeStalkerPortalIdentity,
|
||||
type Playlist,
|
||||
type PlaylistMeta,
|
||||
type PlaylistMetaUpdate,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
|
||||
const STALKER_EDIT_ERROR_KEY_BY_KIND: Readonly<
|
||||
Record<StalkerPortalErrorKind, string>
|
||||
> = {
|
||||
'login-required': 'HOME.STALKER_PORTAL.LOGIN_REQUIRED',
|
||||
'login-rejected': 'HOME.STALKER_PORTAL.LOGIN_REJECTED',
|
||||
'device-conflict': 'HOME.STALKER_PORTAL.DEVICE_CONFLICT',
|
||||
blocked: 'HOME.STALKER_PORTAL.PORTAL_REFUSED',
|
||||
'auth-failed': 'HOME.STALKER_PORTAL.AUTH_FAILED',
|
||||
};
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AppStalkerPlaylistConnectionEditorService implements StalkerPlaylistConnectionEditor {
|
||||
private readonly discovery = inject(StalkerPortalDiscoveryService);
|
||||
private readonly portalRepair = inject(StalkerPortalRepairService);
|
||||
private readonly stalkerSession = inject(StalkerSessionService);
|
||||
private readonly stalkerStore = inject(StalkerStore);
|
||||
private readonly translate = inject(TranslateService);
|
||||
|
||||
async applyResolvedConnection(playlist: PlaylistMetaUpdate): Promise<void> {
|
||||
// Edit discovery is newer and more authoritative than a lazy repair
|
||||
// remembered for the previous connection. Applying that override to
|
||||
// the resolved row could turn a credential-only A→A edit back into
|
||||
// the repair's old A→B result.
|
||||
const runtimePlaylist = this.toRuntimePlaylist(playlist);
|
||||
this.portalRepair.retireForPlaylistEdit(runtimePlaylist._id);
|
||||
const sessionReplacement =
|
||||
this.stalkerSession.replaceSessionAfterEdit(runtimePlaylist);
|
||||
|
||||
let activePlaylistUpdate = Promise.resolve();
|
||||
if (this.stalkerStore.currentPlaylist()?._id === runtimePlaylist._id) {
|
||||
// `setCurrentPlaylist` patches the signal-store snapshot before
|
||||
// its optional SQLite sync awaits, and re-points/stops the active
|
||||
// watchdog synchronously. The next catalog/playback request can
|
||||
// therefore no longer use the pre-edit endpoint or identity.
|
||||
activePlaylistUpdate =
|
||||
this.stalkerStore.setCurrentPlaylist(runtimePlaylist);
|
||||
}
|
||||
await Promise.all([sessionReplacement, activePlaylistUpdate]);
|
||||
}
|
||||
|
||||
async resolveConnection(
|
||||
playlist: PlaylistMeta
|
||||
): Promise<StalkerPlaylistConnectionResult> {
|
||||
const identity = normalizeStalkerPortalIdentity({
|
||||
serialNumber: playlist.stalkerSerialNumber,
|
||||
deviceId1: playlist.stalkerDeviceId1,
|
||||
deviceId2: playlist.stalkerDeviceId2,
|
||||
signature1: playlist.stalkerSignature1,
|
||||
signature2: playlist.stalkerSignature2,
|
||||
});
|
||||
const normalizedPlaylist: PlaylistMetaUpdate = {
|
||||
...playlist,
|
||||
stalkerSerialNumber: identity.serialNumber ?? '',
|
||||
stalkerDeviceId1: identity.deviceId1 ?? '',
|
||||
stalkerDeviceId2: identity.deviceId2 ?? '',
|
||||
stalkerSignature1: identity.signature1 ?? '',
|
||||
stalkerSignature2: identity.signature2 ?? '',
|
||||
};
|
||||
const outcome = await this.discovery.discover(
|
||||
playlist.portalUrl ?? '',
|
||||
playlist.macAddress ?? '',
|
||||
identity,
|
||||
{
|
||||
credentials: {
|
||||
username: playlist.username ?? '',
|
||||
password: playlist.password ?? '',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (outcome.status === 'unreachable') {
|
||||
return {
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.UNREACHABLE,
|
||||
message: this.translate.instant(
|
||||
'HOME.STALKER_PORTAL.EDIT_UNREACHABLE'
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (outcome.status === 'auth-rejected') {
|
||||
return {
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.AUTH_REJECTED,
|
||||
message: this.buildAuthErrorMessage(outcome.error),
|
||||
};
|
||||
}
|
||||
|
||||
const resolvedPlaylist: PlaylistMetaUpdate = {
|
||||
...normalizedPlaylist,
|
||||
portalUrl: outcome.portalUrl,
|
||||
isFullStalkerPortal: outcome.isFullStalkerPortal,
|
||||
};
|
||||
|
||||
if (!outcome.isFullStalkerPortal) {
|
||||
return {
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.RESOLVED,
|
||||
playlist: {
|
||||
...resolvedPlaylist,
|
||||
stalkerSessionPatch: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!outcome.token) {
|
||||
return {
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.AUTH_REJECTED,
|
||||
message: this.translate.instant(
|
||||
'HOME.STALKER_PORTAL.AUTH_FAILED'
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.RESOLVED,
|
||||
playlist: {
|
||||
...resolvedPlaylist,
|
||||
stalkerSessionPatch: {
|
||||
stalkerToken: outcome.token,
|
||||
stalkerSessionIdentity:
|
||||
stalkerSessionFingerprint(resolvedPlaylist),
|
||||
stalkerWatchdogTimeout:
|
||||
outcome.watchdogTimeoutSeconds ??
|
||||
STALKER_WATCHDOG_DEFAULT_PERIOD_SECONDS,
|
||||
stalkerTimeslot: outcome.timeslotSeconds ?? 0,
|
||||
stalkerAccountInfo: outcome.accountInfo
|
||||
? {
|
||||
login: outcome.accountInfo.login,
|
||||
expireDate: outcome.accountInfo.expire_date,
|
||||
tariffPlanName:
|
||||
outcome.accountInfo.tariff_plan_name,
|
||||
status: outcome.accountInfo.status,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private buildAuthErrorMessage(error: unknown): string {
|
||||
const portalError = asStalkerPortalError(error);
|
||||
const headline = this.translate.instant(
|
||||
portalError
|
||||
? STALKER_EDIT_ERROR_KEY_BY_KIND[portalError.kind]
|
||||
: 'HOME.STALKER_PORTAL.AUTH_FAILED'
|
||||
);
|
||||
|
||||
if (!portalError?.portalText) {
|
||||
return headline;
|
||||
}
|
||||
|
||||
return `${headline} ${this.translate.instant(
|
||||
'HOME.STALKER_PORTAL.PORTAL_MESSAGE',
|
||||
{ message: portalError.portalText }
|
||||
)}`;
|
||||
}
|
||||
|
||||
private toRuntimePlaylist(update: PlaylistMetaUpdate): Playlist {
|
||||
const { stalkerSessionPatch, ...metadata } = update;
|
||||
return {
|
||||
lastUsage: '',
|
||||
...metadata,
|
||||
...(stalkerSessionPatch !== undefined
|
||||
? {
|
||||
stalkerToken: stalkerSessionPatch?.stalkerToken,
|
||||
stalkerSessionIdentity:
|
||||
stalkerSessionPatch?.stalkerSessionIdentity,
|
||||
stalkerWatchdogTimeout:
|
||||
stalkerSessionPatch?.stalkerWatchdogTimeout,
|
||||
stalkerTimeslot: stalkerSessionPatch?.stalkerTimeslot,
|
||||
stalkerAccountInfo:
|
||||
stalkerSessionPatch?.stalkerAccountInfo,
|
||||
}
|
||||
: {}),
|
||||
} as Playlist;
|
||||
}
|
||||
}
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "التوقيع 2 (اختياري)",
|
||||
"SIGNATURE_HINT": "64 حرفًا سداسيًا عشريًا - استخدمه إذا كان المزود يتطلب توقيعات للتحقق من الجهاز",
|
||||
"SERVER_URL": "رابط الخادم",
|
||||
"URL_VALIDATION_ERROR": "يجب أن يكون الرابط صالحًا مع بروتوكول (مثال: http://example.com/stalker_portal)",
|
||||
"URL_HINT": "أدخل اسم مضيف أو عنوانًا ينتهي بـ /c أو portal.php أو server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "أدخل رابطًا صالحًا يبدأ بـ http:// أو https://.",
|
||||
"ADD": "إضافة",
|
||||
"VALIDATING": "جارٍ التحقق من البوابة...",
|
||||
"CREDENTIALS_HINT": "مطلوب فقط عندما تطلب البوابة اسم مستخدم وكلمة مرور",
|
||||
"AUTH_FAILED": "فشلت المصادقة مع البوابة. تحقق من الرابط وعنوان MAC.",
|
||||
"EDIT_UNREACHABLE": "لم تستجب البوابة. لم يتم حفظ أي تغييرات. تحقق من الرابط وحاول مرة أخرى.",
|
||||
"LOGIN_REQUIRED": "تتطلب هذه البوابة اسم مستخدم وكلمة مرور. املأ حقلي اسم المستخدم وكلمة المرور وأعد المحاولة.",
|
||||
"LOGIN_REJECTED": "رفضت البوابة اسم المستخدم وكلمة المرور.",
|
||||
"PORTAL_REFUSED": "رفضت البوابة الوصول لهذا الجهاز.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "التوقيع 2 (اختياري)",
|
||||
"SIGNATURE_HINT": "64 حرف hex - استعملو إذا المزود كيتطلب توقيعات التحقق من الجهاز",
|
||||
"SERVER_URL": "رابط الخادم",
|
||||
"URL_VALIDATION_ERROR": "خاص يكون رابط صحيح مع البروتوكول (مثلا http://example.com/c ولا https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "دخل الهوست ولا عنوان كيسالي بـ /c ولا portal.php ولا server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "دخل رابط صحيح كيبدا بـ http:// ولا https://.",
|
||||
"ADD": "زيد",
|
||||
"VALIDATING": "جاري التحقق من البوابة...",
|
||||
"CREDENTIALS_HINT": "خاصين غير إذا البوابة كتطلب اسم المستخدم وكلمة المرور",
|
||||
"AUTH_FAILED": "فشل تسجيل الدخول للبوابة. تحقق من الرابط وعنوان MAC.",
|
||||
"EDIT_UNREACHABLE": "البوابة ما جاوباتش. ما تحفظ حتى تغيير. تأكد من الرابط وعاود المحاولة.",
|
||||
"LOGIN_REQUIRED": "هاد البوابة كتطلب اسم المستخدم وكلمة المرور. عمّر خانات اسم المستخدم وكلمة المرور وحاول مرة أخرى.",
|
||||
"LOGIN_REJECTED": "البوابة رفضات اسم المستخدم وكلمة المرور.",
|
||||
"PORTAL_REFUSED": "البوابة رفضات الوصول لهاد الجهاز.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Подпіс 2 (неабавязкова)",
|
||||
"SIGNATURE_HINT": "64 шаснаццатковыя сімвалы — выкарыстоўвайце, калі правайдэр патрабуе подпісы для верыфікацыі прылады",
|
||||
"SERVER_URL": "URL сервера",
|
||||
"URL_VALIDATION_ERROR": "Павінен быць сапраўдны URL-адрас з пратаколам (напрыклад, http://example.com/c або https://example.com/stalker_portal/c).",
|
||||
"URL_HINT": "Увядзіце хост або адрас, які заканчваецца на /c, portal.php ці server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Увядзіце правільны URL, які пачынаецца з http:// або https://.",
|
||||
"ADD": "Дадаць",
|
||||
"VALIDATING": "Праверка партала...",
|
||||
"CREDENTIALS_HINT": "Патрэбна толькі тады, калі партал запытвае лагін і пароль",
|
||||
"AUTH_FAILED": "Не ўдалося аўтэнтыфікавацца на партале. Праверце URL і MAC-адрас.",
|
||||
"EDIT_UNREACHABLE": "Партал не адказаў. Змены не захаваны. Праверце URL і паўтарыце спробу.",
|
||||
"LOGIN_REQUIRED": "Гэты партал патрабуе лагін і пароль. Запоўніце палі імя карыстальніка і пароля і паспрабуйце яшчэ раз.",
|
||||
"LOGIN_REJECTED": "Партал адхіліў лагін і пароль.",
|
||||
"PORTAL_REFUSED": "Партал адмовіў у доступе для гэтай прылады.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Signatur 2 (optional)",
|
||||
"SIGNATURE_HINT": "64 Hex-Zeichen – verwenden, wenn der Anbieter Geräte-Verifizierungssignaturen verlangt",
|
||||
"SERVER_URL": "Server-URL",
|
||||
"URL_VALIDATION_ERROR": "Sollte eine gültige URL mit Protokoll sein (z. B. http://example.com/c oder https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Geben Sie einen Host oder eine Adresse mit /c, portal.php oder server/load.php ein.",
|
||||
"URL_VALIDATION_ERROR": "Geben Sie eine gültige URL mit http:// oder https:// ein.",
|
||||
"ADD": "Hinzufügen",
|
||||
"VALIDATING": "Portal wird überprüft …",
|
||||
"CREDENTIALS_HINT": "Nur erforderlich, wenn das Portal Benutzername und Passwort verlangt",
|
||||
"AUTH_FAILED": "Authentifizierung am Portal fehlgeschlagen. Überprüfen Sie die URL und die MAC-Adresse.",
|
||||
"EDIT_UNREACHABLE": "Das Portal hat nicht geantwortet. Es wurden keine Änderungen gespeichert. Prüfen Sie die URL und versuchen Sie es erneut.",
|
||||
"LOGIN_REQUIRED": "Dieses Portal erfordert Benutzername und Passwort. Füllen Sie die Felder Benutzername und Passwort aus und versuchen Sie es erneut.",
|
||||
"LOGIN_REJECTED": "Das Portal hat Benutzername und Passwort abgelehnt.",
|
||||
"PORTAL_REFUSED": "Das Portal hat den Zugriff für dieses Gerät verweigert.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Υπογραφή 2 (Προαιρετικό)",
|
||||
"SIGNATURE_HINT": "64 δεκαεξαδικοί χαρακτήρες - χρησιμοποιήστε εάν ο πάροχος απαιτεί υπογραφές επαλήθευσης συσκευής",
|
||||
"SERVER_URL": "Διεύθηνση URL σέρβερ",
|
||||
"URL_VALIDATION_ERROR": "Θα πρέπει να είναι μια έγκυρη διεύθυνση URL με πρωτόκολλο (π.χ. http://example.com/c ή https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Εισαγάγετε έναν host ή μια διεύθυνση που τελειώνει σε /c, portal.php ή server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Εισαγάγετε μια έγκυρη διεύθυνση URL που αρχίζει με http:// ή https://.",
|
||||
"ADD": "Προσθήκη",
|
||||
"VALIDATING": "Επικύρωση πύλης...",
|
||||
"CREDENTIALS_HINT": "Απαιτείται μόνο όταν η πύλη ζητά όνομα χρήστη και κωδικό πρόσβασης",
|
||||
"AUTH_FAILED": "Η ταυτοποίηση με την πύλη απέτυχε. Ελέγξτε τη διεύθυνση URL και τη διεύθυνση MAC.",
|
||||
"EDIT_UNREACHABLE": "Η πύλη δεν απάντησε. Δεν αποθηκεύτηκαν αλλαγές. Ελέγξτε τη διεύθυνση URL και δοκιμάστε ξανά.",
|
||||
"LOGIN_REQUIRED": "Αυτή η πύλη απαιτεί όνομα χρήστη και κωδικό πρόσβασης. Συμπληρώστε τα πεδία ονόματος χρήστη και κωδικού πρόσβασης και δοκιμάστε ξανά.",
|
||||
"LOGIN_REJECTED": "Η πύλη απέρριψε το όνομα χρήστη και τον κωδικό πρόσβασης.",
|
||||
"PORTAL_REFUSED": "Η πύλη αρνήθηκε την πρόσβαση για αυτήν τη συσκευή.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Signature 2 (Optional)",
|
||||
"SIGNATURE_HINT": "64 hex characters - use if provider requires device verification signatures",
|
||||
"SERVER_URL": "Server URL",
|
||||
"URL_VALIDATION_ERROR": "Should be a valid url with protocol (e.g. http://example.com/c or https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Enter a host or an address ending in /c, portal.php, or server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Enter a valid http:// or https:// URL.",
|
||||
"ADD": "Add",
|
||||
"VALIDATING": "Validating portal...",
|
||||
"CREDENTIALS_HINT": "Only needed when the portal asks for a login and password",
|
||||
"AUTH_FAILED": "Failed to authenticate with the portal. Check the URL and MAC address.",
|
||||
"EDIT_UNREACHABLE": "The portal did not respond. No changes were saved. Check the URL and try again.",
|
||||
"LOGIN_REQUIRED": "This portal requires a login and password. Fill in the username and password fields and try again.",
|
||||
"LOGIN_REJECTED": "The portal rejected the login and password.",
|
||||
"PORTAL_REFUSED": "The portal refused access for this device.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Firma 2 (opcional)",
|
||||
"SIGNATURE_HINT": "64 caracteres hexadecimales — úsalo si el proveedor requiere firmas de verificación del dispositivo",
|
||||
"SERVER_URL": "URL del servidor",
|
||||
"URL_VALIDATION_ERROR": "Debe ser una URL válida con protocolo (por ejemplo, http://example.com/c o https://example.com/stalker_portal/c).",
|
||||
"URL_HINT": "Introduce un host o una dirección que termine en /c, portal.php o server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Introduce una URL válida que empiece por http:// o https://.",
|
||||
"ADD": "Agregar",
|
||||
"VALIDATING": "Validando portal…",
|
||||
"CREDENTIALS_HINT": "Solo es necesario cuando el portal pide usuario y contraseña",
|
||||
"AUTH_FAILED": "No se pudo autenticar con el portal. Verifica la URL y la dirección MAC.",
|
||||
"EDIT_UNREACHABLE": "El portal no respondió. No se guardaron los cambios. Comprueba la URL e inténtalo de nuevo.",
|
||||
"LOGIN_REQUIRED": "Este portal requiere usuario y contraseña. Completa los campos de nombre de usuario y contraseña e inténtalo de nuevo.",
|
||||
"LOGIN_REJECTED": "El portal rechazó el usuario y la contraseña.",
|
||||
"PORTAL_REFUSED": "El portal denegó el acceso a este dispositivo.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Signature 2 (optionnelle)",
|
||||
"SIGNATURE_HINT": "64 caractères hexadécimaux — à utiliser si le fournisseur exige des signatures de vérification d'appareil",
|
||||
"SERVER_URL": "URL du serveur",
|
||||
"URL_VALIDATION_ERROR": "Doit être une URL valide avec un protocole (par exemple http://example.com/c ou https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Saisissez un hôte ou une adresse se terminant par /c, portal.php ou server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Saisissez une URL valide commençant par http:// ou https://.",
|
||||
"ADD": "Ajouter",
|
||||
"VALIDATING": "Validation du portail…",
|
||||
"CREDENTIALS_HINT": "Nécessaire uniquement lorsque le portail demande un identifiant et un mot de passe",
|
||||
"AUTH_FAILED": "Échec de l'authentification auprès du portail. Vérifiez l'URL et l'adresse MAC.",
|
||||
"EDIT_UNREACHABLE": "Le portail n'a pas répondu. Aucune modification n'a été enregistrée. Vérifiez l'URL et réessayez.",
|
||||
"LOGIN_REQUIRED": "Ce portail nécessite un identifiant et un mot de passe. Renseignez les champs nom d'utilisateur et mot de passe, puis réessayez.",
|
||||
"LOGIN_REJECTED": "Le portail a rejeté l'identifiant et le mot de passe.",
|
||||
"PORTAL_REFUSED": "Le portail a refusé l'accès pour cet appareil.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "2. aláírás (nem kötelező)",
|
||||
"SIGNATURE_HINT": "64 hexadecimális karakter – csak akkor adja meg, ha a szolgáltató eszközellenőrzési aláírást kér",
|
||||
"SERVER_URL": "Kiszolgáló URL-címe",
|
||||
"URL_VALIDATION_ERROR": "Adjon meg protokollt is tartalmazó, érvényes URL-címet (például http://example.com/c vagy https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Adjon meg egy gazdagépet vagy /c, portal.php vagy server/load.php végű címet.",
|
||||
"URL_VALIDATION_ERROR": "Adjon meg egy http:// vagy https:// kezdetű érvényes URL-címet.",
|
||||
"ADD": "Hozzáadás",
|
||||
"VALIDATING": "Portál ellenőrzése…",
|
||||
"CREDENTIALS_HINT": "Csak akkor szükséges, ha a portál felhasználónevet és jelszót kér",
|
||||
"AUTH_FAILED": "Nem sikerült a hitelesítés a portálon. Ellenőrizze az URL-címet és a MAC-címet.",
|
||||
"EDIT_UNREACHABLE": "A portál nem válaszolt. A módosítások nem lettek mentve. Ellenőrizze az URL-címet, és próbálja újra.",
|
||||
"LOGIN_REQUIRED": "Ez a portál felhasználónevet és jelszót igényel. Töltse ki a felhasználónév és a jelszó mezőt, majd próbálja újra.",
|
||||
"LOGIN_REJECTED": "A portál elutasította a felhasználónevet és a jelszót.",
|
||||
"PORTAL_REFUSED": "A portál megtagadta a hozzáférést ettől az eszköztől.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Firma 2 (opzionale)",
|
||||
"SIGNATURE_HINT": "64 caratteri esadecimali — usa se il fornitore richiede firme di verifica del dispositivo",
|
||||
"SERVER_URL": "URL server",
|
||||
"URL_VALIDATION_ERROR": "Dovrebbe essere una URL valida con protocollo (es. http://esempio.it/c o https://esempio.it/stalker_portal/c)",
|
||||
"URL_HINT": "Inserisci un host o un indirizzo che termini con /c, portal.php o server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Inserisci un URL valido che inizi con http:// o https://.",
|
||||
"ADD": "Aggiungi",
|
||||
"VALIDATING": "Validazione del portale...",
|
||||
"CREDENTIALS_HINT": "Necessario solo se il portale richiede nome utente e password",
|
||||
"AUTH_FAILED": "Impossibile autenticarsi con il portale. Controlla l'URL e l'indirizzo MAC.",
|
||||
"EDIT_UNREACHABLE": "Il portale non ha risposto. Nessuna modifica è stata salvata. Controlla l'URL e riprova.",
|
||||
"LOGIN_REQUIRED": "Questo portale richiede nome utente e password. Compila i campi nome utente e password e riprova.",
|
||||
"LOGIN_REJECTED": "Il portale ha rifiutato il nome utente e la password.",
|
||||
"PORTAL_REFUSED": "Il portale ha negato l'accesso a questo dispositivo.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "署名 2(任意)",
|
||||
"SIGNATURE_HINT": "16進64文字。プロバイダーがデバイス検証署名を要求する場合に使用",
|
||||
"SERVER_URL": "サーバーURL",
|
||||
"URL_VALIDATION_ERROR": "プロトコルを含む有効なURLである必要があります(例:http://example.com/c または https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "ホスト、または /c、portal.php、server/load.php で終わるアドレスを入力してください。",
|
||||
"URL_VALIDATION_ERROR": "http:// または https:// で始まる有効なURLを入力してください。",
|
||||
"ADD": "追加",
|
||||
"VALIDATING": "ポータルを検証中...",
|
||||
"CREDENTIALS_HINT": "ポータルがユーザー名とパスワードを要求する場合のみ必要",
|
||||
"AUTH_FAILED": "ポータルでの認証に失敗しました。URLとMACアドレスを確認してください。",
|
||||
"EDIT_UNREACHABLE": "ポータルが応答しませんでした。変更は保存されていません。URLを確認して再試行してください。",
|
||||
"LOGIN_REQUIRED": "このポータルにはユーザー名とパスワードが必要です。ユーザー名とパスワードの欄を入力して、もう一度お試しください。",
|
||||
"LOGIN_REJECTED": "ポータルがユーザー名とパスワードを拒否しました。",
|
||||
"PORTAL_REFUSED": "ポータルがこのデバイスのアクセスを拒否しました。",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "서명 2 (선택 사항)",
|
||||
"SIGNATURE_HINT": "16진수 64자 - 공급자가 기기 검증 서명을 요구할 때 사용",
|
||||
"SERVER_URL": "서버 URL",
|
||||
"URL_VALIDATION_ERROR": "프로토콜이 포함된 유효한 URL이어야 합니다(예: http://example.com/c 또는 https://example.com/stalker_portal/c).",
|
||||
"URL_HINT": "호스트 또는 /c, portal.php, server/load.php로 끝나는 주소를 입력하세요.",
|
||||
"URL_VALIDATION_ERROR": "http:// 또는 https://로 시작하는 올바른 URL을 입력하세요.",
|
||||
"ADD": "추가하다",
|
||||
"VALIDATING": "포털을 검증하는 중...",
|
||||
"CREDENTIALS_HINT": "포털이 사용자 이름과 비밀번호를 요구하는 경우에만 필요",
|
||||
"AUTH_FAILED": "포털 인증에 실패했습니다. URL과 MAC 주소를 확인하세요.",
|
||||
"EDIT_UNREACHABLE": "포털이 응답하지 않았습니다. 변경 사항이 저장되지 않았습니다. URL을 확인하고 다시 시도하세요.",
|
||||
"LOGIN_REQUIRED": "이 포털에는 사용자 이름과 비밀번호가 필요합니다. 사용자 이름과 비밀번호 필드를 입력한 후 다시 시도하세요.",
|
||||
"LOGIN_REJECTED": "포털이 사용자 이름과 비밀번호를 거부했습니다.",
|
||||
"PORTAL_REFUSED": "포털이 이 기기의 접근을 거부했습니다.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Handtekening 2 (optioneel)",
|
||||
"SIGNATURE_HINT": "64 hex-tekens — gebruiken als de provider apparaatverificatiehandtekeningen vereist",
|
||||
"SERVER_URL": "Server URL",
|
||||
"URL_VALIDATION_ERROR": "Moet een geldige URL zijn met protocol (bijv. http://example.com/c o https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Voer een host of adres in dat eindigt op /c, portal.php of server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Voer een geldige URL in die begint met http:// of https://.",
|
||||
"ADD": "Toevoegen",
|
||||
"VALIDATING": "Portaal valideren...",
|
||||
"CREDENTIALS_HINT": "Alleen nodig als het portaal om een gebruikersnaam en wachtwoord vraagt",
|
||||
"AUTH_FAILED": "Aanmelden bij het portaal is mislukt. Controleer de URL en het MAC-adres.",
|
||||
"EDIT_UNREACHABLE": "Het portaal reageerde niet. Er zijn geen wijzigingen opgeslagen. Controleer de URL en probeer het opnieuw.",
|
||||
"LOGIN_REQUIRED": "Dit portaal vereist een gebruikersnaam en wachtwoord. Vul de velden gebruikersnaam en wachtwoord in en probeer het opnieuw.",
|
||||
"LOGIN_REJECTED": "Het portaal heeft de gebruikersnaam en het wachtwoord geweigerd.",
|
||||
"PORTAL_REFUSED": "Het portaal heeft de toegang voor dit apparaat geweigerd.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Sygnatura 2 (opcjonalnie)",
|
||||
"SIGNATURE_HINT": "64 znaki szesnastkowe – użyj, jeśli dostawca wymaga sygnatur weryfikacji urządzenia",
|
||||
"SERVER_URL": "URL serwera",
|
||||
"URL_VALIDATION_ERROR": "Powinien to być poprawny URL z protokołem (np. http://example.com/c lub https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Wprowadź host lub adres zakończony /c, portal.php albo server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Wprowadź poprawny URL zaczynający się od http:// lub https://.",
|
||||
"ADD": "Dodaj",
|
||||
"VALIDATING": "Walidacja portalu...",
|
||||
"CREDENTIALS_HINT": "Potrzebne tylko wtedy, gdy portal wymaga loginu i hasła",
|
||||
"AUTH_FAILED": "Nie udało się uwierzytelnić w portalu. Sprawdź URL i adres MAC.",
|
||||
"EDIT_UNREACHABLE": "Portal nie odpowiedział. Nie zapisano żadnych zmian. Sprawdź URL i spróbuj ponownie.",
|
||||
"LOGIN_REQUIRED": "Ten portal wymaga loginu i hasła. Wypełnij pola nazwy użytkownika i hasła i spróbuj ponownie.",
|
||||
"LOGIN_REJECTED": "Portal odrzucił login i hasło.",
|
||||
"PORTAL_REFUSED": "Portal odmówił dostępu temu urządzeniu.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Assinatura 2 (opcional)",
|
||||
"SIGNATURE_HINT": "64 caracteres hexadecimais - use se o provedor exigir assinaturas de verificação do dispositivo",
|
||||
"SERVER_URL": "URL do servidor",
|
||||
"URL_VALIDATION_ERROR": "Deve ser uma URL válida com protocolo (por exemplo, http://example.com/c ou https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "Introduza um host ou endereço terminado em /c, portal.php ou server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Introduza um URL válido que comece por http:// ou https://.",
|
||||
"ADD": "Adicionar",
|
||||
"VALIDATING": "Validando portal...",
|
||||
"CREDENTIALS_HINT": "Necessário apenas quando o portal exige login e senha",
|
||||
"AUTH_FAILED": "Falha na autenticação com o portal. Verifique a URL e o endereço MAC.",
|
||||
"EDIT_UNREACHABLE": "O portal não respondeu. Nenhuma alteração foi guardada. Verifique o URL e tente novamente.",
|
||||
"LOGIN_REQUIRED": "Este portal exige login e senha. Preencha os campos de nome de usuário e senha e tente novamente.",
|
||||
"LOGIN_REJECTED": "O portal rejeitou o login e a senha.",
|
||||
"PORTAL_REFUSED": "O portal recusou o acesso para este dispositivo.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "Подпись 2 (необязательно)",
|
||||
"SIGNATURE_HINT": "64 шестнадцатеричных символа — используйте, если провайдер требует подписи проверки устройства",
|
||||
"SERVER_URL": "URL сервера",
|
||||
"URL_VALIDATION_ERROR": "Должен быть действительный URL-адрес с протоколом (например, http://example.com/c или https://example.com/stalker_portal/c).",
|
||||
"URL_HINT": "Введите хост или адрес, оканчивающийся на /c, portal.php или server/load.php.",
|
||||
"URL_VALIDATION_ERROR": "Введите корректный URL, начинающийся с http:// или https://.",
|
||||
"ADD": "Добавить",
|
||||
"VALIDATING": "Проверка портала…",
|
||||
"CREDENTIALS_HINT": "Требуется, только если портал запрашивает логин и пароль",
|
||||
"AUTH_FAILED": "Не удалось авторизоваться на портале. Проверьте URL и MAC-адрес.",
|
||||
"EDIT_UNREACHABLE": "Портал не ответил. Изменения не сохранены. Проверьте URL и повторите попытку.",
|
||||
"LOGIN_REQUIRED": "Этот портал требует логин и пароль. Заполните поля имени пользователя и пароля и повторите попытку.",
|
||||
"LOGIN_REJECTED": "Портал отклонил логин и пароль.",
|
||||
"PORTAL_REFUSED": "Портал отказал в доступе этому устройству.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "İmza 2 (İsteğe Bağlı)",
|
||||
"SIGNATURE_HINT": "64 onaltılık karakter - sağlayıcı cihaz doğrulama imzaları gerektiriyorsa kullanın",
|
||||
"SERVER_URL": "Sunucu URL'si",
|
||||
"URL_VALIDATION_ERROR": "Geçerli bir URL olmalıdır (örn. http://example.com/c veya https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "/c, portal.php veya server/load.php ile biten bir ana makine ya da adres girin.",
|
||||
"URL_VALIDATION_ERROR": "http:// veya https:// ile başlayan geçerli bir URL girin.",
|
||||
"ADD": "Ekle",
|
||||
"VALIDATING": "Portal doğrulanıyor...",
|
||||
"CREDENTIALS_HINT": "Yalnızca portal kullanıcı adı ve parola istediğinde gereklidir",
|
||||
"AUTH_FAILED": "Portalda kimlik doğrulaması başarısız oldu. URL'yi ve MAC adresini kontrol edin.",
|
||||
"EDIT_UNREACHABLE": "Portal yanıt vermedi. Hiçbir değişiklik kaydedilmedi. URL'yi kontrol edip tekrar deneyin.",
|
||||
"LOGIN_REQUIRED": "Bu portal kullanıcı adı ve parola gerektiriyor. Kullanıcı adı ve parola alanlarını doldurup tekrar deneyin.",
|
||||
"LOGIN_REJECTED": "Portal, kullanıcı adı ve parolayı reddetti.",
|
||||
"PORTAL_REFUSED": "Portal bu cihaz için erişimi reddetti.",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "签名 2(可选)",
|
||||
"SIGNATURE_HINT": "64 位十六进制字符 — 如提供商需要设备验证签名时使用",
|
||||
"SERVER_URL": "服务器 URL",
|
||||
"URL_VALIDATION_ERROR": "应该是带有协议的有效网址(例如 http://example.com/c 或 https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "请输入主机或以 /c、portal.php、server/load.php 结尾的地址。",
|
||||
"URL_VALIDATION_ERROR": "请输入以 http:// 或 https:// 开头的有效 URL。",
|
||||
"ADD": "添加",
|
||||
"VALIDATING": "正在验证门户…",
|
||||
"CREDENTIALS_HINT": "仅在门户要求登录名和密码时才需要填写",
|
||||
"AUTH_FAILED": "门户身份验证失败。请检查 URL 和 MAC 地址。",
|
||||
"EDIT_UNREACHABLE": "门户没有响应。未保存任何更改。请检查 URL 后重试。",
|
||||
"LOGIN_REQUIRED": "此门户需要登录名和密码。请填写用户名和密码字段后重试。",
|
||||
"LOGIN_REJECTED": "门户拒绝了该登录名和密码。",
|
||||
"PORTAL_REFUSED": "门户拒绝了此设备的访问。",
|
||||
|
||||
@@ -199,11 +199,13 @@
|
||||
"SIGNATURE_2": "簽章 2(選填)",
|
||||
"SIGNATURE_HINT": "64 個十六進位字元——若供應商要求裝置驗證簽章時請填寫",
|
||||
"SERVER_URL": "伺服器網址",
|
||||
"URL_VALIDATION_ERROR": "必須是有效的網址並包含協定(例如 http://example.com/c 或 https://example.com/stalker_portal/c)",
|
||||
"URL_HINT": "請輸入主機或以 /c、portal.php、server/load.php 結尾的位址。",
|
||||
"URL_VALIDATION_ERROR": "請輸入以 http:// 或 https:// 開頭的有效網址。",
|
||||
"ADD": "新增",
|
||||
"VALIDATING": "正在驗證入口網站...",
|
||||
"CREDENTIALS_HINT": "僅在入口網站要求登入名稱與密碼時才需填寫",
|
||||
"AUTH_FAILED": "入口網站驗證失敗。請檢查網址與 MAC 位址。",
|
||||
"EDIT_UNREACHABLE": "入口網站沒有回應。未儲存任何變更。請檢查網址後再試一次。",
|
||||
"LOGIN_REQUIRED": "此入口網站需要登入名稱與密碼。請填寫使用者名稱與密碼欄位後重試。",
|
||||
"LOGIN_REJECTED": "入口網站拒絕了此登入名稱與密碼。",
|
||||
"PORTAL_REFUSED": "入口網站拒絕了此裝置的存取。",
|
||||
|
||||
Reference in new issue
Block a user