Add browser playback diagnostics and MPV/VLC fallback (#921)

* feat(playback): add codec diagnostics fallback
Entire-Checkpoint: f957cd9849e0

* fix(playback): address diagnostics review feedback
Entire-Checkpoint: f957cd9849e0

* fix(playback): ignore recoverable hls diagnostics
Entire-Checkpoint: f957cd9849e0

* fix(playback): polish diagnostic fallback banner
Entire-Checkpoint: f957cd9849e0

* fix(xtream): preserve live category search scope
Entire-Checkpoint: f957cd9849e0
This commit is contained in:
4gray authored and GitHub committed 2026-05-10 17:27:54 +02:00
1 parent 5f4cf01467
commit 6305dcf00e
66 files changed
+2857 -206

No files matched your search

@@ -108,7 +108,8 @@ export type LaunchedElectronApp = {
};
export const test = base.extend<ElectronFixtures>({
dataDir: async ({}, use) => {
dataDir: async ({ browserName }, use) => {
void browserName;
const dataDir = mkdtempSync(join(tmpdir(), 'iptvnator-electron-e2e-'));
await use(dataDir);
@@ -948,24 +949,22 @@ export async function clickCategoryByNameExact(
const category = await pickPreferredCategory(categories);
await expect(category).toBeVisible();
await expect(category).toBeEnabled();
await category.scrollIntoViewIfNeeded();
const categoryId =
(await category.getAttribute('data-category-id'))?.trim() ?? '';
await category.click();
await expect
.poll(async () => {
const pathname = new URL(page.url()).pathname;
const isSelected =
(await category.getAttribute('aria-current')) === 'true';
return (
isSelected ||
(categoryId.length > 0 &&
(pathname.endsWith(`/${categoryId}`) ||
pathname.includes(`/${categoryId}/`)))
);
})
.toBe(true);
const selectedCategory =
categoryId.length > 0
? page
.locator(
`app-workspace-context-panel .category-item[data-category-id="${categoryId}"]:visible`
)
.first()
: category;
await expect(selectedCategory).toHaveAttribute('aria-current', 'true', {
timeout: 20000,
});
}
export function sourceRowByTitle(page: Page, title: string): Locator {
+23 -14
View File
@@ -519,7 +519,12 @@ test.describe('Electron Workspace Search', () => {
).toHaveClass(/(^|\s)active(\s|$)/);
await fillWorkspaceSearch(app.mainWindow, '');
await expectQueryParamAbsent(app.mainWindow, 'q');
await clickCategoryByNameExact(app.mainWindow, sample.categoryName);
await expectWorkspaceSearchScope(
app.mainWindow,
`Live TV / ${sample.categoryName}`
);
await fillWorkspaceSearch(app.mainWindow, sample.targetTitle);
await expectPathname(
@@ -1352,24 +1357,22 @@ async function clickCategoryByNameExact(
const category = await pickPreferredCategory(categories);
await expect(category).toBeVisible();
await expect(category).toBeEnabled();
await category.scrollIntoViewIfNeeded();
const categoryId =
(await category.getAttribute('data-category-id'))?.trim() ?? '';
await category.click();
await expect
.poll(async () => {
const pathname = new URL(page.url()).pathname;
const isSelected =
(await category.getAttribute('aria-current')) === 'true';
return (
isSelected ||
(categoryId.length > 0 &&
(pathname.endsWith(`/${categoryId}`) ||
pathname.includes(`/${categoryId}/`)))
);
})
.toBe(true);
const selectedCategory =
categoryId.length > 0
? page
.locator(
`app-workspace-context-panel .category-item[data-category-id="${categoryId}"]:visible`
)
.first()
: category;
await expect(selectedCategory).toHaveAttribute('aria-current', 'true', {
timeout: 20000,
});
}
async function openWorkspaceSection(page: Page, label: string): Promise<void> {
@@ -1770,3 +1773,9 @@ async function expectQueryParam(
.poll(() => new URL(page.url()).searchParams.get(name))
.toBe(value);
}
async function expectQueryParamAbsent(page: Page, name: string): Promise<void> {
await expect
.poll(() => new URL(page.url()).searchParams.has(name))
.toBe(false);
}