mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 14:16:16 -08:00
perf(portals): fast-fail requests to portal hosts that stopped answering (#1421)
This commit is contained in:
1 parent
2d7811eb5f
commit
e3f72f7dce
50 files changed
+3073
-174
No files matched your search
@@ -17,6 +17,7 @@ import {
|
||||
} from '@iptvnator/services';
|
||||
import { ChannelActions, PlaylistActions } from '@iptvnator/m3u-state';
|
||||
import {
|
||||
CONNECTIVITY_GUARD_RESET,
|
||||
ELECTRON_BRIDGE_SECURITY_ERROR_CODES,
|
||||
PLAYLIST_UPDATE,
|
||||
Playlist,
|
||||
@@ -688,4 +689,49 @@ describe('PlaylistRefreshActionService', () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('clears the connectivity guard before deleting the cached Xtream catalog', async () => {
|
||||
// This refresh deletes the catalog and then forces a route bootstrap
|
||||
// whose status request an open guard would fast-fail — leaving the user
|
||||
// with no catalog at all until the cooldown expires.
|
||||
const item = {
|
||||
_id: 'xtream-guard',
|
||||
title: 'Guarded Xtream',
|
||||
serverUrl: 'http://panel.example:8080',
|
||||
username: 'user',
|
||||
password: 'secret',
|
||||
macAddress: undefined,
|
||||
portalUrl: undefined,
|
||||
} as unknown as PlaylistMeta;
|
||||
let confirmPromise: Promise<void> | undefined;
|
||||
const order: string[] = [];
|
||||
dataService.sendIpcEvent.mockImplementation((event: string) => {
|
||||
order.push(`ipc:${event}`);
|
||||
return Promise.resolve({ success: true });
|
||||
});
|
||||
databaseService.deleteXtreamPlaylistContent.mockImplementation(() => {
|
||||
order.push('deleteXtreamPlaylistContent');
|
||||
return Promise.resolve({
|
||||
hiddenCategories: [],
|
||||
favorites: [],
|
||||
recentlyViewed: [],
|
||||
sourcePins: [],
|
||||
});
|
||||
});
|
||||
dialogService.openConfirmDialog.mockImplementation(
|
||||
({ onConfirm }: { onConfirm?: () => Promise<void> }) => {
|
||||
confirmPromise = onConfirm?.();
|
||||
}
|
||||
);
|
||||
|
||||
service.refresh(item);
|
||||
await confirmPromise;
|
||||
|
||||
expect(order[0]).toBe(`ipc:${CONNECTIVITY_GUARD_RESET}`);
|
||||
expect(order).toContain('deleteXtreamPlaylistContent');
|
||||
expect(dataService.sendIpcEvent).toHaveBeenCalledWith(
|
||||
CONNECTIVITY_GUARD_RESET,
|
||||
{ url: item.serverUrl }
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
isDbAbortError,
|
||||
PlaybackPositionService,
|
||||
PlaylistRefreshService,
|
||||
resetHostConnectivityGuard,
|
||||
RuntimeCapabilitiesService,
|
||||
SettingsStore,
|
||||
XtreamPendingRestoreService,
|
||||
@@ -124,6 +125,16 @@ export class PlaylistRefreshActionService {
|
||||
});
|
||||
|
||||
try {
|
||||
// Before anything destructive: this refresh deletes the
|
||||
// cached catalog and then forces a route bootstrap whose
|
||||
// status request would be fast-failed by an open
|
||||
// connectivity guard, leaving the user with no catalog at
|
||||
// all until the cooldown expires.
|
||||
await resetHostConnectivityGuard(
|
||||
this.dataService,
|
||||
item.serverUrl
|
||||
);
|
||||
|
||||
this.snackBar.open(
|
||||
this.translate.instant(
|
||||
'HOME.PLAYLISTS.REFRESH_XTREAM_DIALOG.STARTED'
|
||||
|
||||
+53
-2
@@ -26,7 +26,11 @@ import {
|
||||
SortOrder,
|
||||
SortService,
|
||||
} from '@iptvnator/services';
|
||||
import { PLAYLIST_UPDATE, PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
CONNECTIVITY_GUARD_RESET,
|
||||
PLAYLIST_UPDATE,
|
||||
PlaylistMeta,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
RENDERER_PERFORMANCE_PHASE_HOOK_KEY,
|
||||
type RendererPerformancePhaseEvent,
|
||||
@@ -442,7 +446,11 @@ describe('RecentPlaylistsComponent busy state', () => {
|
||||
);
|
||||
|
||||
component.refreshXtreamPlaylist(item);
|
||||
await Promise.resolve();
|
||||
// The refresh clears the connectivity guard before it starts deleting,
|
||||
// so drain the microtask queue rather than counting exact ticks.
|
||||
for (let index = 0; index < 6; index += 1) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
expect(component.isRefreshPending(item._id)).toBe(true);
|
||||
expect(component.getBusyMessage(item)).toBe(
|
||||
@@ -676,4 +684,47 @@ describe('RecentPlaylistsComponent busy state', () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('clears the connectivity guard before deleting the cached Xtream catalog', async () => {
|
||||
// Second, independent implementation of the destructive refresh (the
|
||||
// Workspace sources page). Same consequence as the shared action: the
|
||||
// catalog is already gone by the time an open guard fast-fails the
|
||||
// re-import bootstrap.
|
||||
const item = {
|
||||
_id: 'xtream-guard',
|
||||
title: 'Guarded Xtream',
|
||||
serverUrl: 'http://panel.example:8080',
|
||||
} as PlaylistMeta;
|
||||
const order: string[] = [];
|
||||
let confirmPromise: Promise<void> | undefined;
|
||||
|
||||
dataService.sendIpcEvent.mockImplementation((event: string) => {
|
||||
order.push(`ipc:${event}`);
|
||||
return Promise.resolve({ success: true });
|
||||
});
|
||||
databaseService.deleteXtreamPlaylistContent.mockImplementation(() => {
|
||||
order.push('deleteXtreamPlaylistContent');
|
||||
return Promise.resolve({
|
||||
hiddenCategories: [],
|
||||
favorites: [],
|
||||
recentlyViewed: [],
|
||||
sourcePins: [],
|
||||
});
|
||||
});
|
||||
dialogService.openConfirmDialog.mockImplementation(
|
||||
({ onConfirm }: { onConfirm?: () => Promise<void> }) => {
|
||||
confirmPromise = onConfirm?.();
|
||||
}
|
||||
);
|
||||
|
||||
component.refreshXtreamPlaylist(item);
|
||||
await confirmPromise;
|
||||
|
||||
expect(order[0]).toBe(`ipc:${CONNECTIVITY_GUARD_RESET}`);
|
||||
expect(order).toContain('deleteXtreamPlaylistContent');
|
||||
expect(dataService.sendIpcEvent).toHaveBeenCalledWith(
|
||||
CONNECTIVITY_GUARD_RESET,
|
||||
{ url: item.serverUrl }
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
PlaybackPositionService,
|
||||
PlaylistDeleteActionService,
|
||||
PlaylistRefreshService,
|
||||
resetHostConnectivityGuard,
|
||||
RuntimeCapabilitiesService,
|
||||
SortBy,
|
||||
SortService,
|
||||
@@ -366,6 +367,16 @@ export class RecentPlaylistsComponent {
|
||||
this.databaseService.createOperationId('xtream-refresh');
|
||||
|
||||
try {
|
||||
// Before anything destructive, and for the same reason as
|
||||
// the shared refresh action: this deletes the cached catalog
|
||||
// and then navigates to re-import it, and an open
|
||||
// connectivity guard would fast-fail that bootstrap — the
|
||||
// user would be left with no catalog at all.
|
||||
await resetHostConnectivityGuard(
|
||||
this.dataService,
|
||||
item.serverUrl
|
||||
);
|
||||
|
||||
// Show immediate feedback — deletion can take several seconds
|
||||
// for large playlists.
|
||||
this.snackBar.open(
|
||||
|
||||
Reference in new issue
Block a user