mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
feat(playback): recommend recovery actions (#1374)
* docs(playback): design recovery recommendations * docs(playback): plan recovery recommendations * refactor(playback): extract diagnostic utilities * feat(playback): define recovery recommendation contracts * feat(playback): rank recovery recommendations * feat(playback): track session recovery attempts * feat(playback): identify content recovery sessions * feat(ui): add ranked playback diagnostic panel * feat(playback): switch temporarily to recommended players * test(playback): cover temporary player recommendation * test(playback): verify recommendation capability guards * docs(playback): document recovery recommendations * fix(playback): keep recovery keys credential-free * fix(playback): remove derived tracking ownership * fix(playback): preserve distinct recovery fallbacks * fix(playback): reset resume for new sources * fix(playback): preserve desktop recovery guidance * docs(playback): clarify recovery policy exceptions * fix(playback): reject stale progress updates * fix(playback): keep protected recovery guidance neutral * test(playback): cover stale progress output * fix(playback): neutralize protected diagnostic copy * fix(playback): harden runtime guidance ownership * fix(playback): stabilize recovery application ownership * fix(ci): classify playback util coverage * fix(e2e): preserve playback fixture bytes
This commit is contained in:
1 parent
5e4f2ca3dd
commit
fd96b85c19
176 files changed
+15228
-1908
No files matched your search
@@ -1,6 +1,10 @@
|
||||
import { createServer, Server } from 'http';
|
||||
import { readFileSync } from 'fs';
|
||||
import { basename, join } from 'path';
|
||||
import type {
|
||||
ExternalPlayerName,
|
||||
PlayerContentInfo,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
channelItemByTitle,
|
||||
closeElectronApp,
|
||||
@@ -19,7 +23,47 @@ import {
|
||||
* context). Uses the shared offline fixtures from apps/web-e2e/src/fixtures.
|
||||
*/
|
||||
|
||||
const FIXTURE_DIR = join(workspaceRoot, 'apps/web-e2e/src/fixtures/dash');
|
||||
const DASH_FIXTURE_DIR = join(workspaceRoot, 'apps/web-e2e/src/fixtures/dash');
|
||||
const PLAYBACK_FIXTURE_DIR = join(
|
||||
workspaceRoot,
|
||||
'apps/web-e2e/src/fixtures/playback'
|
||||
);
|
||||
|
||||
type PlaybackFixture = {
|
||||
readonly contentType: string;
|
||||
readonly path: string;
|
||||
};
|
||||
|
||||
const FIXTURE_FILES: ReadonlyMap<string, PlaybackFixture> = new Map([
|
||||
[
|
||||
'clearkey.mpd',
|
||||
{
|
||||
contentType: 'application/dash+xml',
|
||||
path: join(DASH_FIXTURE_DIR, 'clearkey.mpd'),
|
||||
},
|
||||
],
|
||||
[
|
||||
'clearkey-video.mp4',
|
||||
{
|
||||
contentType: 'video/mp4',
|
||||
path: join(DASH_FIXTURE_DIR, 'clearkey-video.mp4'),
|
||||
},
|
||||
],
|
||||
[
|
||||
'clearkey-audio.mp4',
|
||||
{
|
||||
contentType: 'video/mp4',
|
||||
path: join(DASH_FIXTURE_DIR, 'clearkey-audio.mp4'),
|
||||
},
|
||||
],
|
||||
[
|
||||
'unsupported.mkv',
|
||||
{
|
||||
contentType: 'video/matroska',
|
||||
path: join(PLAYBACK_FIXTURE_DIR, 'unsupported.mkv'),
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const CLEARKEY_KID = '00112233445566778899aabbccddeeff';
|
||||
const CLEARKEY_KEY = 'ffeeddccbbaa99887766554433221100';
|
||||
@@ -29,31 +73,33 @@ type DashFixtureServer = {
|
||||
origin: string;
|
||||
};
|
||||
|
||||
/** Serves the DASH fixture directory with HTTP Range support (Shaka fetches
|
||||
* init segments and the sidx via byte ranges). */
|
||||
/** Serves the explicit offline fixture allowlist with HTTP Range support
|
||||
* (Shaka fetches init segments and the sidx via byte ranges). */
|
||||
async function startDashFixtureServer(): Promise<DashFixtureServer> {
|
||||
const server: Server = createServer((request, response) => {
|
||||
const pathname = (request.url ?? '').split('?')[0];
|
||||
const fileName = basename(pathname);
|
||||
const fixture = FIXTURE_FILES.get(fileName);
|
||||
if (!fixture) {
|
||||
response.writeHead(404);
|
||||
response.end('not found');
|
||||
return;
|
||||
}
|
||||
let body: Buffer;
|
||||
try {
|
||||
body = readFileSync(join(FIXTURE_DIR, fileName));
|
||||
body = readFileSync(fixture.path);
|
||||
} catch {
|
||||
response.writeHead(404);
|
||||
response.end('not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = fileName.endsWith('.mpd')
|
||||
? 'application/dash+xml'
|
||||
: 'video/mp4';
|
||||
const range = /bytes=(\d+)-(\d+)?/.exec(
|
||||
request.headers.range ?? ''
|
||||
);
|
||||
const range = /bytes=(\d+)-(\d+)?/.exec(request.headers.range ?? '');
|
||||
if (!range) {
|
||||
response.writeHead(200, {
|
||||
'Content-Type': contentType,
|
||||
'Content-Type': fixture.contentType,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Length': body.length,
|
||||
});
|
||||
response.end(body);
|
||||
@@ -64,8 +110,9 @@ async function startDashFixtureServer(): Promise<DashFixtureServer> {
|
||||
const end = range[2] ? Number(range[2]) : body.length - 1;
|
||||
const chunk = body.subarray(start, end + 1);
|
||||
response.writeHead(206, {
|
||||
'Content-Type': contentType,
|
||||
'Content-Type': fixture.contentType,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Range': `bytes ${start}-${end}/${body.length}`,
|
||||
'Content-Length': chunk.length,
|
||||
});
|
||||
@@ -106,6 +153,8 @@ function buildDashPlaylist(origin: string): string {
|
||||
'#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha',
|
||||
'#KODIPROP:inputstream.adaptive.license_key=https://license.example.com/wv',
|
||||
`${origin}/clearkey.mpd`,
|
||||
'#EXTINF:-1 tvg-id="unsupported-mkv" group-title="DASH",Unsupported MKV',
|
||||
`${origin}/unsupported.mkv`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
@@ -122,13 +171,179 @@ async function importDashPlaylistFromText(
|
||||
await waitForM3uCatalog(app.mainWindow);
|
||||
}
|
||||
|
||||
test('@electron @dash ClearKey DASH plays inline and unsupported DRM surfaces a diagnostic', async ({
|
||||
type ExternalPlayerIpcArguments = [
|
||||
url: string,
|
||||
title: string,
|
||||
thumbnail: string | undefined,
|
||||
userAgent: string | undefined,
|
||||
referer: string | undefined,
|
||||
origin: string | undefined,
|
||||
contentInfo: PlayerContentInfo | undefined,
|
||||
startTime: number | undefined,
|
||||
headers: Record<string, string> | undefined,
|
||||
];
|
||||
|
||||
type CapturedExternalPlayerLaunch = {
|
||||
args: ExternalPlayerIpcArguments;
|
||||
player: ExternalPlayerName;
|
||||
};
|
||||
|
||||
type PlaybackRecommendationCaptureSnapshot = {
|
||||
completed: number;
|
||||
launches: CapturedExternalPlayerLaunch[];
|
||||
released: boolean;
|
||||
};
|
||||
|
||||
type PlaybackRecommendationCaptureState =
|
||||
PlaybackRecommendationCaptureSnapshot & {
|
||||
releaseFirstResponse: (() => void) | undefined;
|
||||
waitForFirstResponse: Promise<void>;
|
||||
};
|
||||
|
||||
async function installPlaybackRecommendationLaunchCapture(
|
||||
app: LaunchedElectronApp
|
||||
): Promise<void> {
|
||||
await app.electronApp.evaluate(({ ipcMain }) => {
|
||||
let releaseFirstResponse: (() => void) | undefined;
|
||||
const waitForFirstResponse = new Promise<void>((resolve) => {
|
||||
releaseFirstResponse = resolve;
|
||||
});
|
||||
const state: PlaybackRecommendationCaptureState = {
|
||||
completed: 0,
|
||||
launches: [],
|
||||
released: false,
|
||||
releaseFirstResponse,
|
||||
waitForFirstResponse,
|
||||
};
|
||||
const globalRef = globalThis as typeof globalThis & {
|
||||
__playbackRecommendationCapture?: PlaybackRecommendationCaptureState;
|
||||
};
|
||||
globalRef.__playbackRecommendationCapture = state;
|
||||
|
||||
const captureLaunch =
|
||||
(player: ExternalPlayerName) =>
|
||||
async (
|
||||
_event: unknown,
|
||||
url: string,
|
||||
title: string,
|
||||
thumbnail?: string,
|
||||
userAgent?: string,
|
||||
referer?: string,
|
||||
origin?: string,
|
||||
contentInfo?: PlayerContentInfo,
|
||||
startTime?: number,
|
||||
headers?: Record<string, string>
|
||||
) => {
|
||||
const invocation = state.launches.push({
|
||||
args: [
|
||||
url,
|
||||
title,
|
||||
thumbnail,
|
||||
userAgent,
|
||||
referer,
|
||||
origin,
|
||||
contentInfo,
|
||||
startTime,
|
||||
headers,
|
||||
],
|
||||
player,
|
||||
});
|
||||
|
||||
if (invocation === 1 && !state.released) {
|
||||
await state.waitForFirstResponse;
|
||||
}
|
||||
|
||||
state.completed += 1;
|
||||
const now = new Date().toISOString();
|
||||
return {
|
||||
canClose: false,
|
||||
id: `e2e-recommended-${player}-${invocation}`,
|
||||
player,
|
||||
startedAt: now,
|
||||
status: 'opened',
|
||||
streamUrl: url,
|
||||
thumbnail: thumbnail ?? null,
|
||||
title,
|
||||
updatedAt: now,
|
||||
};
|
||||
};
|
||||
|
||||
ipcMain.removeHandler('OPEN_MPV_PLAYER');
|
||||
ipcMain.removeHandler('OPEN_VLC_PLAYER');
|
||||
ipcMain.handle('OPEN_MPV_PLAYER', captureLaunch('mpv'));
|
||||
ipcMain.handle('OPEN_VLC_PLAYER', captureLaunch('vlc'));
|
||||
});
|
||||
}
|
||||
|
||||
async function getPlaybackRecommendationCapture(
|
||||
app: LaunchedElectronApp
|
||||
): Promise<PlaybackRecommendationCaptureSnapshot> {
|
||||
return app.electronApp.evaluate(() => {
|
||||
const globalRef = globalThis as typeof globalThis & {
|
||||
__playbackRecommendationCapture?: PlaybackRecommendationCaptureState;
|
||||
};
|
||||
const capture = globalRef.__playbackRecommendationCapture;
|
||||
if (!capture) {
|
||||
throw new Error(
|
||||
'Playback recommendation capture is not installed.'
|
||||
);
|
||||
}
|
||||
return {
|
||||
completed: capture.completed,
|
||||
launches: capture.launches,
|
||||
released: capture.released,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function releasePlaybackRecommendationCapture(
|
||||
app: LaunchedElectronApp
|
||||
): Promise<void> {
|
||||
await app.electronApp.evaluate(() => {
|
||||
const globalRef = globalThis as typeof globalThis & {
|
||||
__playbackRecommendationCapture?: PlaybackRecommendationCaptureState;
|
||||
};
|
||||
const capture = globalRef.__playbackRecommendationCapture;
|
||||
if (!capture || capture.released) {
|
||||
return;
|
||||
}
|
||||
capture.released = true;
|
||||
capture.releaseFirstResponse?.();
|
||||
capture.releaseFirstResponse = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
test('@electron @dash ClearKey DASH filters DRM fallback and eligible MKV opens in MPV', async ({
|
||||
dataDir,
|
||||
}) => {
|
||||
const fixtureServer = await startDashFixtureServer();
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
const unsupportedFixtureResponse = await app.mainWindow.evaluate(
|
||||
async (url) => {
|
||||
const response = await fetch(url);
|
||||
const body = await response.text();
|
||||
return {
|
||||
body,
|
||||
bodyBytes: new TextEncoder().encode(body).byteLength,
|
||||
contentLength: response.headers.get('content-length'),
|
||||
contentType: response.headers.get('content-type'),
|
||||
rendererProtocol: window.location.protocol,
|
||||
status: response.status,
|
||||
};
|
||||
},
|
||||
`${fixtureServer.origin}/unsupported.mkv`
|
||||
);
|
||||
expect(unsupportedFixtureResponse).toEqual({
|
||||
body: 'This fixture intentionally declares Matroska without playable media bytes.\n',
|
||||
bodyBytes: 75,
|
||||
contentLength: '75',
|
||||
contentType: 'video/matroska',
|
||||
rendererProtocol: 'file:',
|
||||
status: 200,
|
||||
});
|
||||
|
||||
await importDashPlaylistFromText(
|
||||
app,
|
||||
buildDashPlaylist(fixtureServer.origin)
|
||||
@@ -160,12 +375,124 @@ test('@electron @dash ClearKey DASH plays inline and unsupported DRM surfaces a
|
||||
await channelItemByTitle(app.mainWindow, 'Widevine DASH')
|
||||
.first()
|
||||
.click();
|
||||
const banner = app.mainWindow.getByTestId(
|
||||
'playback-diagnostic-banner'
|
||||
);
|
||||
const banner = app.mainWindow.getByTestId('playback-diagnostic-banner');
|
||||
await expect(banner).toBeVisible({ timeout: 15_000 });
|
||||
await expect(banner).toContainText(/encrypted or DRM-protected/i);
|
||||
await expect(
|
||||
banner.locator('[data-test-id="playback-fallback-mpv"]')
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
banner.locator('[data-test-id="playback-fallback-vlc"]')
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
banner.locator('[data-test-id^="playback-recommendation-"]')
|
||||
).toHaveCount(0);
|
||||
const widevineDetails = banner.locator(
|
||||
'[data-test-id="playback-diagnostic-details"]'
|
||||
);
|
||||
await widevineDetails.locator('summary').click();
|
||||
await expect(widevineDetails).toContainText('drm-or-encryption');
|
||||
|
||||
await installPlaybackRecommendationLaunchCapture(app);
|
||||
await channelItemByTitle(app.mainWindow, 'Unsupported MKV')
|
||||
.first()
|
||||
.click();
|
||||
await expect(banner).toContainText(
|
||||
/container is likely unsupported by the browser player/i,
|
||||
{ timeout: 15_000 }
|
||||
);
|
||||
const mkvDetails = banner.locator(
|
||||
'[data-test-id="playback-diagnostic-details"]'
|
||||
);
|
||||
await mkvDetails.locator('summary').click();
|
||||
await expect(mkvDetails).toContainText('unsupported-container');
|
||||
|
||||
const mpvFallback = banner.locator(
|
||||
'[data-test-id="playback-fallback-mpv"]'
|
||||
);
|
||||
const vlcFallback = banner.locator(
|
||||
'[data-test-id="playback-fallback-vlc"]'
|
||||
);
|
||||
await expect(mpvFallback).toBeVisible();
|
||||
await expect(vlcFallback).toBeVisible();
|
||||
await expect(mpvFallback).toHaveClass(
|
||||
/web-player-diagnostic__player-card--primary/
|
||||
);
|
||||
await expect(vlcFallback).not.toHaveClass(
|
||||
/web-player-diagnostic__player-card--primary/
|
||||
);
|
||||
|
||||
const expectedLaunches = [
|
||||
{
|
||||
args: [
|
||||
`${fixtureServer.origin}/unsupported.mkv`,
|
||||
'Unsupported MKV',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
],
|
||||
player: 'mpv',
|
||||
},
|
||||
] satisfies CapturedExternalPlayerLaunch[];
|
||||
expect(await getPlaybackRecommendationCapture(app)).toEqual({
|
||||
completed: 0,
|
||||
launches: [],
|
||||
released: false,
|
||||
});
|
||||
|
||||
await mpvFallback.click();
|
||||
await expect
|
||||
.poll(() => getPlaybackRecommendationCapture(app), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toEqual({
|
||||
completed: 0,
|
||||
launches: expectedLaunches,
|
||||
released: false,
|
||||
});
|
||||
expect(
|
||||
(await getPlaybackRecommendationCapture(app)).launches.filter(
|
||||
({ player }) => player === 'vlc'
|
||||
)
|
||||
).toHaveLength(0);
|
||||
await expect(mpvFallback).toHaveCount(0);
|
||||
await expect(vlcFallback).toHaveClass(
|
||||
/web-player-diagnostic__player-card--primary/
|
||||
);
|
||||
|
||||
await releasePlaybackRecommendationCapture(app);
|
||||
await expect
|
||||
.poll(() => getPlaybackRecommendationCapture(app), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toEqual({
|
||||
completed: 1,
|
||||
launches: expectedLaunches,
|
||||
released: true,
|
||||
});
|
||||
await app.mainWindow.evaluate(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
requestAnimationFrame(() =>
|
||||
requestAnimationFrame(() => resolve())
|
||||
);
|
||||
})
|
||||
);
|
||||
expect(await getPlaybackRecommendationCapture(app)).toEqual({
|
||||
completed: 1,
|
||||
launches: expectedLaunches,
|
||||
released: true,
|
||||
});
|
||||
await expect(mpvFallback).toHaveCount(0);
|
||||
await expect(vlcFallback).toHaveClass(
|
||||
/web-player-diagnostic__player-card--primary/
|
||||
);
|
||||
} finally {
|
||||
await releasePlaybackRecommendationCapture(app).catch(() => undefined);
|
||||
await closeElectronApp(app);
|
||||
await fixtureServer.close();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ import playwright from 'eslint-plugin-playwright';
|
||||
import baseConfig from '../../eslint.config.mjs';
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ['**/fixtures/playback/corrupt.ts'],
|
||||
},
|
||||
playwright.configs['flat/recommended'],
|
||||
...baseConfig,
|
||||
{
|
||||
|
||||
@@ -150,4 +150,13 @@ test('@web @m3u @dash unsupported DRM shows the encryption diagnostic', async ({
|
||||
const banner = page.locator('[data-test-id="playback-diagnostic-banner"]');
|
||||
await expect(banner).toBeVisible({ timeout: 15_000 });
|
||||
await expect(banner).toContainText(/encrypted or DRM-protected/i);
|
||||
await expect(
|
||||
banner.locator('[data-test-id="playback-fallback-mpv"]')
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
banner.locator('[data-test-id="playback-fallback-vlc"]')
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
banner.locator('[data-test-id^="playback-recommendation-"]')
|
||||
).toHaveCount(0);
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
This is intentionally not an MPEG transport stream segment.
|
||||
@@ -0,0 +1,7 @@
|
||||
#EXTM3U
|
||||
#EXT-X-VERSION:3
|
||||
#EXT-X-TARGETDURATION:1
|
||||
#EXT-X-MEDIA-SEQUENCE:0
|
||||
#EXTINF:1,
|
||||
corrupt.ts
|
||||
#EXT-X-ENDLIST
|
||||
@@ -0,0 +1 @@
|
||||
This fixture intentionally declares Matroska without playable media bytes.
|
||||
@@ -0,0 +1,141 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import type { Page } from '@playwright/test';
|
||||
import { expect, test } from './fixtures';
|
||||
|
||||
const FIXTURE_DIR = join(__dirname, 'fixtures/playback');
|
||||
const FIXTURE_HOST = 'https://playback-fixture.local';
|
||||
const FATAL_HLS_PLAYLIST = [
|
||||
'#EXTM3U',
|
||||
'#EXTINF:-1 tvg-id="fatal-hls" group-title="Playback",Fatal HLS',
|
||||
`${FIXTURE_HOST}/fatal-media.m3u8`,
|
||||
].join('\n');
|
||||
|
||||
const PLAYBACK_FIXTURES = new Map([
|
||||
[
|
||||
'/fatal-media.m3u8',
|
||||
{
|
||||
body: readFileSync(join(FIXTURE_DIR, 'fatal-media.m3u8')),
|
||||
contentType: 'application/vnd.apple.mpegurl',
|
||||
},
|
||||
],
|
||||
[
|
||||
'/corrupt.ts',
|
||||
{
|
||||
body: readFileSync(join(FIXTURE_DIR, 'corrupt.ts')),
|
||||
contentType: 'video/mp2t',
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
test.use({
|
||||
launchOptions: {
|
||||
args: ['--autoplay-policy=no-user-gesture-required'],
|
||||
},
|
||||
serviceWorkers: 'block',
|
||||
});
|
||||
|
||||
test.skip(
|
||||
({ browserName }) => browserName !== 'chromium',
|
||||
'HLS recovery coverage targets Chromium'
|
||||
);
|
||||
|
||||
async function servePlaybackFixtures(page: Page): Promise<void> {
|
||||
await page.route(`${FIXTURE_HOST}/**`, async (route) => {
|
||||
const fixture = PLAYBACK_FIXTURES.get(
|
||||
new URL(route.request().url()).pathname
|
||||
);
|
||||
if (!fixture) {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'not found',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: fixture.contentType,
|
||||
body: fixture.body,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function openSettings(page: Page): Promise<void> {
|
||||
await page.getByRole('link', { name: 'Open settings' }).click();
|
||||
await page.waitForURL(/\/workspace\/settings$/);
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Back', exact: true })
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async function selectAndSaveHtml5(page: Page): Promise<void> {
|
||||
const playerSelect = page.locator('[data-test-id="select-video-player"]');
|
||||
await playerSelect.click();
|
||||
await page
|
||||
.getByRole('option', { name: 'HTML5 video player', exact: true })
|
||||
.click();
|
||||
|
||||
const saveButton = page.getByRole('button', { name: 'Save changes' });
|
||||
await saveButton.click();
|
||||
await expect(saveButton).toBeDisabled();
|
||||
}
|
||||
|
||||
async function importFatalHlsPlaylist(page: Page): Promise<void> {
|
||||
await page.getByRole('button', { name: 'Add playlist' }).click();
|
||||
const dialog = page.getByRole('dialog');
|
||||
await expect(dialog).toBeVisible();
|
||||
await dialog.getByRole('radio', { name: /Raw m3u text/i }).click();
|
||||
await dialog
|
||||
.getByLabel('Insert m3u(8) playlist as text')
|
||||
.fill(FATAL_HLS_PLAYLIST);
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/workspace\/playlists\/.+\/all$/),
|
||||
dialog.getByRole('button', { name: 'Import', exact: true }).click(),
|
||||
]);
|
||||
await expect(page.getByText('1 channel')).toBeVisible();
|
||||
}
|
||||
|
||||
test('@web @m3u @playback temporarily switches to the recommended player', async ({
|
||||
page,
|
||||
}) => {
|
||||
await servePlaybackFixtures(page);
|
||||
await page.goto('/');
|
||||
|
||||
await openSettings(page);
|
||||
await selectAndSaveHtml5(page);
|
||||
await page.getByRole('button', { name: 'Back', exact: true }).click();
|
||||
await page.waitForURL(/\/workspace\/dashboard$/);
|
||||
|
||||
await importFatalHlsPlaylist(page);
|
||||
await page.getByText('1. Fatal HLS', { exact: true }).click();
|
||||
|
||||
const banner = page.locator('[data-test-id="playback-diagnostic-banner"]');
|
||||
const videoJsRecommendation = page.locator(
|
||||
'[data-test-id="playback-recommendation-videojs"]'
|
||||
);
|
||||
await expect(banner).toBeVisible({ timeout: 15_000 });
|
||||
await expect(
|
||||
banner.locator('[data-test-id="playback-diagnostic-details"]')
|
||||
).toContainText(
|
||||
/HLS\.js[\s\S]*type=mediaError[\s\S]*details=fragParsingError[\s\S]*disposition=fatal/
|
||||
);
|
||||
await expect(videoJsRecommendation).toBeVisible();
|
||||
await expect(videoJsRecommendation).toHaveClass(
|
||||
/web-player-diagnostic__player-card--primary/
|
||||
);
|
||||
|
||||
await videoJsRecommendation.click();
|
||||
await expect(page.locator('app-vjs-player')).toBeVisible();
|
||||
|
||||
await openSettings(page);
|
||||
await page.reload();
|
||||
await expect(page).toHaveURL(/\/workspace\/settings$/);
|
||||
const persistedPlayerSelect = page.locator(
|
||||
'[data-test-id="select-video-player"]'
|
||||
);
|
||||
await expect(persistedPlayerSelect).toBeVisible();
|
||||
await expect(persistedPlayerSelect).toBeEnabled();
|
||||
await expect(persistedPlayerSelect).toHaveText('HTML5 video player');
|
||||
});
|
||||
@@ -17,5 +17,6 @@
|
||||
"playwright.config.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.e2e.ts"
|
||||
]
|
||||
],
|
||||
"exclude": ["src/fixtures/playback/corrupt.ts"]
|
||||
}
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "إجراءات الاسترداد الموصى بها",
|
||||
"ACTION_TRY_PLAYER": "جرّب {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "مؤقت لهذا العنصر؛ سيبقى المشغل المحفوظ دون تغيير",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "أعد المحاولة بنفس المشغل بعد فشل مؤقت في التحميل",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "أعد المحاولة لأن الخطأ لم يحدد مشغلًا آخر متوافقًا",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "جرّب مصدرًا آخر متاحًا للمحتوى نفسه",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "يستخدم هذا المشغل محرك تشغيل مختلفًا في المتصفح",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "تدعم المشغلات الأصلية مزيدًا من برامج الترميز والحاويات",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "قد يتجاوز المشغل الأصلي قيود الوصول في المتصفح",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "هذا الهدف متوافق مع بيانات التشفير المتاحة",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "إجراءات الاسترجاع المقترحة",
|
||||
"ACTION_TRY_PLAYER": "جرّب {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "مؤقت غير لهاد المحتوى؛ المشغل المحفوظ غيبقى بلا تغيير",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "عاود جرّب نفس المشغل من بعد مشكل مؤقت فالتحميل",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "عاود جرّب حيث الخطأ ما حدّد حتى مشغل آخر متوافق",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "جرّب مصدر آخر متوفر لنفس المحتوى",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "هاد المشغل كيستعمل محرك تشغيل مختلف فالمتصفح",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "المشغلات الأصلية كتدعم كوديكات وحاويات أكثر",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "يقدر مشغل أصلي يتجاوز قيود الولوج ديال المتصفح",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "هاد الهدف متوافق مع بيانات التشفير المتوفرة",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Рэкамендаваныя дзеянні для аднаўлення",
|
||||
"ACTION_TRY_PLAYER": "Паспрабаваць {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Часова толькі для гэтага элемента; захаваны плэер не зменіцца",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Паўтарыць у тым жа плэеры пасля часовага збою загрузкі",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Паўтарыць, бо памылка не вызначыла іншы сумяшчальны плэер",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Паспрабаваць іншую даступную крыніцу таго ж змесціва",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Гэты плэер выкарыстоўвае іншы рухавік прайгравання ў браўзеры",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Сістэмныя плэеры падтрымліваюць больш кодэкаў і кантэйнераў",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Сістэмны плэер можа абысці абмежаванні доступу браўзера",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Гэтая мэта сумяшчальная з даступнымі данымі шыфравання",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Empfohlene Wiederherstellungsaktionen",
|
||||
"ACTION_TRY_PLAYER": "{{player}} ausprobieren",
|
||||
"ACTION_TRY_PLAYER_HINT": "Nur vorübergehend für diesen Inhalt; der gespeicherte Player bleibt unverändert",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Nach einem vorübergehenden Ladefehler denselben Player erneut versuchen",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Erneut versuchen, da der Fehler keinen anderen kompatiblen Player erkennen ließ",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Eine andere verfügbare Quelle für denselben Inhalt ausprobieren",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Dieser Player verwendet eine andere Browser-Wiedergabe-Engine",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Native Player unterstützen mehr Codecs und Container",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Ein nativer Player kann Zugriffsbeschränkungen des Browsers umgehen",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Dieses Ziel ist mit den verfügbaren Verschlüsselungsdaten kompatibel",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Προτεινόμενες ενέργειες ανάκτησης",
|
||||
"ACTION_TRY_PLAYER": "Δοκιμή του {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Προσωρινά για αυτό το στοιχείο· το αποθηκευμένο πρόγραμμα αναπαραγωγής δεν αλλάζει",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Δοκιμάστε ξανά το ίδιο πρόγραμμα αναπαραγωγής μετά από μια προσωρινή αποτυχία φόρτωσης",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Δοκιμάστε ξανά, επειδή το σφάλμα δεν εντόπισε άλλο συμβατό πρόγραμμα αναπαραγωγής",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Δοκιμάστε μια άλλη διαθέσιμη πηγή για το ίδιο περιεχόμενο",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Αυτό το πρόγραμμα αναπαραγωγής χρησιμοποιεί διαφορετική μηχανή αναπαραγωγής του προγράμματος περιήγησης",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Τα εγγενή προγράμματα αναπαραγωγής υποστηρίζουν περισσότερους κωδικοποιητές και περιέκτες",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Ένα εγγενές πρόγραμμα αναπαραγωγής μπορεί να παρακάμψει περιορισμούς πρόσβασης του προγράμματος περιήγησης",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Αυτός ο στόχος είναι συμβατός με τα διαθέσιμα δεδομένα κρυπτογράφησης",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Recommended recovery actions",
|
||||
"ACTION_TRY_PLAYER": "Try {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Temporary for this item; your saved player stays unchanged",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Retry the same player after a temporary loading failure",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Retry because the failure did not identify another compatible player",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Try another available source for the same content",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "This player uses a different browser playback engine",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Native players support more codecs and containers",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "A native player may avoid browser access restrictions",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "This target is compatible with the available encryption data",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Acciones de recuperación recomendadas",
|
||||
"ACTION_TRY_PLAYER": "Probar {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Temporal para este elemento; el reproductor guardado no cambiará",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Reintentar con el mismo reproductor tras un fallo temporal de carga",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Reintentar porque el error no identificó otro reproductor compatible",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Probar otra fuente disponible para el mismo contenido",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Este reproductor usa un motor de reproducción distinto del navegador",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Los reproductores nativos admiten más códecs y contenedores",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Un reproductor nativo puede evitar las restricciones de acceso del navegador",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Este destino es compatible con los datos de cifrado disponibles",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Actions de récupération recommandées",
|
||||
"ACTION_TRY_PLAYER": "Essayer {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Temporaire pour cet élément ; votre lecteur enregistré reste inchangé",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Réessayer avec le même lecteur après un échec temporaire de chargement",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Réessayer, car l’erreur n’a identifié aucun autre lecteur compatible",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Essayer une autre source disponible pour le même contenu",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Ce lecteur utilise un autre moteur de lecture du navigateur",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Les lecteurs natifs prennent en charge davantage de codecs et de conteneurs",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Un lecteur natif peut contourner les restrictions d’accès du navigateur",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Cette cible est compatible avec les données de chiffrement disponibles",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "A kérést CORS-korlátozás, vegyes tartalom, illetve a szolgáltató fejléc- vagy proxykorlátozásai akadályozhatják. Az MPV vagy a VLC gyakran képes megkerülni a böngészőben működő lejátszó hozzáférési korlátait.",
|
||||
"PWA_DESCRIPTION": "A kérést CORS-korlátozás, vegyes tartalom, illetve a szolgáltató fejléc- vagy proxykorlátozásai akadályozhatják. A PWA nem tud közvetlenül külső lejátszót indítani; válassza az Adatfolyam URL-címének másolása műveletet, majd nyissa meg kézzel MPV-ben, VLC-ben, IINA-ban vagy más lejátszóban."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "Az adatfolyam titkosított vagy DRM-védelemmel ellátott lehet",
|
||||
"DESCRIPTION": "A böngészőben működő lejátszó a jelzett titkosítási vagy kulcshiba miatt nem tudja folytatni a lejátszást. Ha a szolgáltató DRM-védelmet alkalmaz, a külső lejátszás is sikertelen lehet."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Nyissa meg inkább külső lejátszóban",
|
||||
"INLINE_FAILURE_TITLE": "Másolja ki az URL-címet, vagy próbálja újra",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Ajánlott helyreállítási műveletek",
|
||||
"ACTION_TRY_PLAYER": "A(z) {{player}} kipróbálása",
|
||||
"ACTION_TRY_PLAYER_HINT": "Csak ennél az elemnél ideiglenes; a mentett lejátszó változatlan marad",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Ugyanannak a lejátszónak az újrapróbálása ideiglenes betöltési hiba után",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Újrapróbálás, mert a hiba nem azonosított más kompatibilis lejátszót",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Másik elérhető forrás kipróbálása ugyanahhoz a tartalomhoz",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Ez a lejátszó más böngészős lejátszómotort használ",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "A natív lejátszók több kodeket és konténert támogatnak",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Egy natív lejátszó megkerülheti a böngésző hozzáférési korlátozásait",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Ez a cél kompatibilis az elérhető titkosítási adatokkal",
|
||||
"ACTION_OPEN_MPV": "Megnyitás MPV-ben",
|
||||
"ACTION_OPEN_VLC": "Megnyitás VLC-ben",
|
||||
"ACTION_OPEN_MPV_HINT": "beállított lejátszó",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Azioni di ripristino consigliate",
|
||||
"ACTION_TRY_PLAYER": "Prova {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Temporaneo per questo elemento; il lettore salvato resta invariato",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Riprova con lo stesso lettore dopo un errore temporaneo di caricamento",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Riprova perché l’errore non ha identificato un altro lettore compatibile",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Prova un’altra fonte disponibile per lo stesso contenuto",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Questo lettore usa un motore di riproduzione del browser diverso",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "I lettori nativi supportano più codec e contenitori",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Un lettore nativo può evitare le restrizioni di accesso del browser",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Questa destinazione è compatibile con i dati di crittografia disponibili",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "推奨される復旧操作",
|
||||
"ACTION_TRY_PLAYER": "{{player}} を試す",
|
||||
"ACTION_TRY_PLAYER_HINT": "この項目にのみ一時的に適用され、保存済みのプレーヤーは変更されません",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "一時的な読み込みエラーの後、同じプレーヤーで再試行します",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "エラーから別の互換プレーヤーを特定できなかったため、再試行します",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "同じコンテンツで利用可能な別のソースを試します",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "このプレーヤーは別のブラウザー再生エンジンを使用します",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "ネイティブプレーヤーはより多くのコーデックとコンテナに対応しています",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "ネイティブプレーヤーならブラウザーのアクセス制限を回避できる場合があります",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "この対象は利用可能な暗号化データに対応しています",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "권장 복구 작업",
|
||||
"ACTION_TRY_PLAYER": "{{player}} 사용해 보기",
|
||||
"ACTION_TRY_PLAYER_HINT": "이 항목에만 임시로 적용되며 저장된 플레이어는 변경되지 않습니다",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "일시적인 로드 실패 후 같은 플레이어로 다시 시도합니다",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "오류에서 다른 호환 플레이어를 확인할 수 없어 다시 시도합니다",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "같은 콘텐츠에 사용 가능한 다른 소스를 시도합니다",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "이 플레이어는 다른 브라우저 재생 엔진을 사용합니다",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "네이티브 플레이어는 더 많은 코덱과 컨테이너를 지원합니다",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "네이티브 플레이어는 브라우저 접근 제한을 피할 수 있습니다",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "이 대상은 사용 가능한 암호화 데이터와 호환됩니다",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Aanbevolen herstelacties",
|
||||
"ACTION_TRY_PLAYER": "{{player}} proberen",
|
||||
"ACTION_TRY_PLAYER_HINT": "Tijdelijk voor dit item; je opgeslagen speler blijft ongewijzigd",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Dezelfde speler opnieuw proberen na een tijdelijke laadfout",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Opnieuw proberen omdat de fout geen andere compatibele speler heeft vastgesteld",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Een andere beschikbare bron voor dezelfde inhoud proberen",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Deze speler gebruikt een andere afspeelengine in de browser",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Systeemeigen spelers ondersteunen meer codecs en containers",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Een systeemeigen speler kan toegangsbeperkingen van de browser omzeilen",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Dit doel is compatibel met de beschikbare versleutelingsgegevens",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Zalecane działania odzyskiwania",
|
||||
"ACTION_TRY_PLAYER": "Wypróbuj {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Tymczasowo dla tego elementu; zapisany odtwarzacz pozostanie bez zmian",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Ponów próbę w tym samym odtwarzaczu po tymczasowym błędzie wczytywania",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Ponów próbę, ponieważ błąd nie wskazał innego zgodnego odtwarzacza",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Wypróbuj inne dostępne źródło tej samej treści",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Ten odtwarzacz używa innego silnika odtwarzania w przeglądarce",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Natywne odtwarzacze obsługują więcej kodeków i kontenerów",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Natywny odtwarzacz może ominąć ograniczenia dostępu przeglądarki",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Ten cel jest zgodny z dostępnymi danymi szyfrowania",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Ações de recuperação recomendadas",
|
||||
"ACTION_TRY_PLAYER": "Experimentar {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Temporário para este item; o reprodutor salvo permanece inalterado",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Tentar novamente com o mesmo reprodutor após uma falha temporária de carregamento",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Tentar novamente porque o erro não identificou outro reprodutor compatível",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Experimentar outra fonte disponível para o mesmo conteúdo",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Este reprodutor usa um mecanismo de reprodução diferente no navegador",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Os reprodutores nativos oferecem suporte a mais codecs e contêineres",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Um reprodutor nativo pode contornar as restrições de acesso do navegador",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Este destino é compatível com os dados de criptografia disponíveis",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "Запрос может быть заблокирован CORS, mixed content или требованиями провайдера/прокси к заголовкам. MPV или VLC часто обходят такие ограничения браузерного плеера.",
|
||||
"PWA_DESCRIPTION": "Запрос может быть заблокирован CORS, mixed content или требованиями провайдера/прокси к заголовкам. PWA не может запускать внешние плееры напрямую: нажмите «Скопировать URL потока» и откройте его вручную в MPV, VLC, IINA или другом плеере."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "Для этого защищённого потока требуются настройки воспроизведения, которые нельзя передать вместе с URL-адресом потока.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "Поток может быть зашифрован или защищен DRM",
|
||||
"DESCRIPTION": "Браузерный плеер не может продолжить из-за ошибки ключа или шифрования. Внешний плеер тоже может не помочь, если требуется DRM провайдера."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Откройте поток во внешнем плеере",
|
||||
"INLINE_FAILURE_TITLE": "Скопируйте URL или повторите попытку",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Не удалось продолжить защищённое воспроизведение",
|
||||
"RECOMMENDATIONS_LABEL": "Рекомендуемые действия для восстановления",
|
||||
"ACTION_TRY_PLAYER": "Попробовать {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "Временно только для этого элемента; сохранённый плеер не изменится",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Повторить в том же плеере после временной ошибки загрузки",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Повторить, поскольку ошибка не указала другой совместимый плеер",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Попробовать другой доступный источник того же контента",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Этот плеер использует другой движок воспроизведения в браузере",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Нативные плееры поддерживают больше кодеков и контейнеров",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Нативный плеер может обойти ограничения доступа браузера",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Эта цель совместима с доступными данными шифрования",
|
||||
"ACTION_OPEN_MPV": "Открыть в MPV",
|
||||
"ACTION_OPEN_VLC": "Открыть в VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "настроенный плеер",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "Önerilen kurtarma işlemleri",
|
||||
"ACTION_TRY_PLAYER": "{{player}} ile dene",
|
||||
"ACTION_TRY_PLAYER_HINT": "Bu öğe için geçicidir; kayıtlı oynatıcınız değişmeden kalır",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "Geçici bir yükleme hatasından sonra aynı oynatıcıyla yeniden deneyin",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "Hata başka bir uyumlu oynatıcı belirlemediği için yeniden deneyin",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "Aynı içerik için kullanılabilir başka bir kaynak deneyin",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "Bu oynatıcı farklı bir tarayıcı oynatma motoru kullanır",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "Yerel oynatıcılar daha fazla codec bileşenini ve kapsayıcıyı destekler",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "Yerel bir oynatıcı tarayıcı erişim kısıtlamalarını aşabilir",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "Bu hedef kullanılabilir şifreleme verileriyle uyumludur",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "推荐的恢复操作",
|
||||
"ACTION_TRY_PLAYER": "尝试 {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "仅临时用于此项目;已保存的播放器不会更改",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "临时加载失败后使用同一播放器重试",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "重试,因为该错误未能确定其他兼容的播放器",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "为同一内容尝试其他可用源",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "此播放器使用不同的浏览器播放引擎",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "原生播放器支持更多编解码器和容器",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "原生播放器可以避开浏览器访问限制",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "此目标与可用的加密数据兼容",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
@@ -551,6 +551,7 @@
|
||||
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
|
||||
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
|
||||
},
|
||||
"UNTRANSFERABLE_DESCRIPTION": "This protected stream requires playback configuration that cannot be transferred with the stream URL.",
|
||||
"DRM_OR_ENCRYPTION": {
|
||||
"TITLE": "This stream may be encrypted or DRM-protected",
|
||||
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
|
||||
@@ -561,6 +562,17 @@
|
||||
},
|
||||
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
|
||||
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
|
||||
"UNTRANSFERABLE_FAILURE_TITLE": "Protected playback could not continue",
|
||||
"RECOMMENDATIONS_LABEL": "建議的復原操作",
|
||||
"ACTION_TRY_PLAYER": "嘗試 {{player}}",
|
||||
"ACTION_TRY_PLAYER_HINT": "僅暫時套用於此項目;已儲存的播放器不會變更",
|
||||
"REASON_RETRY_TRANSIENT_FAILURE": "暫時載入失敗後使用相同的播放器重試",
|
||||
"REASON_RETRY_UNKNOWN_FAILURE": "重試,因為此錯誤未能識別其他相容的播放器",
|
||||
"REASON_ALTERNATIVE_SOURCE_AVAILABLE": "為相同內容嘗試其他可用來源",
|
||||
"REASON_DIFFERENT_ENGINE_FAMILY": "此播放器使用不同的瀏覽器播放引擎",
|
||||
"REASON_EXTERNAL_CODEC_OR_CONTAINER_SUPPORT": "原生播放器支援更多編解碼器和容器",
|
||||
"REASON_EXTERNAL_BROWSER_ACCESS": "原生播放器可以避開瀏覽器存取限制",
|
||||
"REASON_COMPATIBLE_DRM_PATH": "此目標與可用的加密資料相容",
|
||||
"ACTION_OPEN_MPV": "Open in MPV",
|
||||
"ACTION_OPEN_VLC": "Open in VLC",
|
||||
"ACTION_OPEN_MPV_HINT": "configured player",
|
||||
|
||||
Reference in new issue
Block a user