refactor(xtream): introduce silent actions and improve error handling for Xtream requests

This commit is contained in:
4gray committed 2026-02-16 23:37:05 +01:00
1 parent d6f5561e6b
commit 61ef8e2553
4 files changed
+151 -24

No files matched your search

+63 -11
View File
@@ -11,6 +11,7 @@ import {
Playlist,
PLAYLIST_PARSE_BY_URL,
PLAYLIST_UPDATE,
XtreamCodeActions,
XTREAM_RESPONSE,
XTREAM_REQUEST,
} from 'shared-interfaces';
@@ -24,6 +25,12 @@ export class ElectronService extends DataService {
private readonly snackBar = inject(MatSnackBar);
private readonly store = inject(Store);
private readonly translateService = inject(TranslateService);
private readonly silentXtreamActions = new Set<string>([
XtreamCodeActions.GetAccountInfo,
XtreamCodeActions.GetLiveCategories,
XtreamCodeActions.GetVodCategories,
XtreamCodeActions.GetSeriesCategories,
]);
constructor() {
super();
@@ -263,24 +270,26 @@ export class ElectronService extends DataService {
window.postMessage(result);
return result;
} catch (error: any) {
const isStatusCheck = payload.params?.action === 'get_account_info';
const action = payload.params?.action;
const isSilentAction = action
? this.silentXtreamActions.has(action)
: false;
const normalizedMessage = this.getReadableXtreamErrorMessage(error);
// Log error to console
if (isStatusCheck) {
if (isSilentAction) {
console.log(
'Portal status check failed - portal may be unavailable:',
error.message || error
`Background Xtream action failed (${action ?? 'unknown'}):`,
normalizedMessage
);
} else {
console.error('Xtream request error:', error.message);
console.error('Xtream request error:', normalizedMessage);
}
// Only show snackbar for non-status-check requests
if (!isStatusCheck) {
// Only show snackbar for user-triggered Xtream requests
if (!isSilentAction) {
this.snackBar.open(
`Error: ${error.message ?? 'Failed to connect to Xtream server'}, status: ${
error.status ?? 500
}`,
`Xtream request failed: ${normalizedMessage}`,
'Close',
{
duration: 5000,
@@ -291,11 +300,54 @@ export class ElectronService extends DataService {
return {
type: ERROR,
status: error.status ?? 500,
message: error.message ?? 'Failed to connect to Xtream server',
message: normalizedMessage,
};
}
}
private getReadableXtreamErrorMessage(error: unknown): string {
const fallback = 'Failed to connect to Xtream server';
if (!error) {
return fallback;
}
const maybeError = error as {
message?: unknown;
statusText?: unknown;
status?: unknown;
error?: unknown;
};
if (typeof maybeError.message === 'string') {
if (maybeError.message.includes('[object Object]')) {
if (typeof maybeError.error === 'string') {
return maybeError.error;
}
if (
maybeError.error &&
typeof maybeError.error === 'object' &&
'message' in (maybeError.error as Record<string, unknown>) &&
typeof (maybeError.error as Record<string, unknown>).message ===
'string'
) {
return (maybeError.error as Record<string, string>).message;
}
return fallback;
}
return maybeError.message;
}
if (typeof maybeError.statusText === 'string') {
return maybeError.statusText;
}
if (typeof error === 'string') {
return error;
}
return fallback;
}
removeAllListeners(type: string): void {
if (type === 'all') {
// Unsubscribe from all event listeners
+86 -12
View File
@@ -14,6 +14,7 @@ import {
PLAYLIST_PARSE_BY_URL,
PLAYLIST_UPDATE,
STALKER_REQUEST,
XtreamCodeActions,
XTREAM_REQUEST,
XTREAM_RESPONSE,
} from 'shared-interfaces';
@@ -28,6 +29,12 @@ export class PwaService extends DataService {
private readonly store = inject(Store);
private readonly swUpdate = inject(SwUpdate);
private readonly translateService = inject(TranslateService);
private readonly silentXtreamActions = new Set<string>([
XtreamCodeActions.GetAccountInfo,
XtreamCodeActions.GetLiveCategories,
XtreamCodeActions.GetVodCategories,
XtreamCodeActions.GetSeriesCategories,
]);
/** Proxy URL to avoid CORS issues */
corsProxyUrl = AppConfig.BACKEND_URL;
@@ -186,15 +193,28 @@ export class PwaService extends DataService {
);
if (!(response as any).payload) {
if (payload.params.action === 'get_account_info') {
console.log('Portal status check failed - portal may be unavailable:', (response as any).message || 'No payload received');
return;
const action = payload.params.action;
const isSilentAction = this.silentXtreamActions.has(action);
const normalizedMessage = this.getReadableXtreamErrorMessage(
response
);
if (isSilentAction) {
console.log(
`Background Xtream action failed (${action ?? 'unknown'}):`,
normalizedMessage
);
return {
type: ERROR,
status: (response as any).status ?? 500,
message: normalizedMessage,
};
}
result = {
type: ERROR,
status: (response as any).status,
message: (response as any).message ?? 'Unknown error',
message: normalizedMessage,
};
window.postMessage(result);
} else {
@@ -207,27 +227,81 @@ export class PwaService extends DataService {
}
return result;
} catch (error: any) {
const isStatusCheck = payload.params.action === 'get_account_info';
const action = payload.params.action;
const isSilentAction = this.silentXtreamActions.has(action);
const normalizedMessage = this.getReadableXtreamErrorMessage(error);
// Log error to console
if (isStatusCheck) {
console.log('Portal status check failed - portal may be unavailable:', error.message || error);
return;
if (isSilentAction) {
console.log(
`Background Xtream action failed (${action ?? 'unknown'}):`,
normalizedMessage
);
return {
type: ERROR,
status: error.status ?? 500,
message: normalizedMessage,
};
}
console.error('Xtream request error:', error.message);
console.error('Xtream request error:', normalizedMessage);
this.snackBar.open(
`Error: ${error.message ?? ' Unknown error'}, status: ${
error.status ?? 500
}`,
`Xtream request failed: ${normalizedMessage}`,
'Close',
{
duration: 5000,
}
);
return {
type: ERROR,
status: error.status ?? 500,
message: normalizedMessage,
};
}
}
private getReadableXtreamErrorMessage(error: unknown): string {
const fallback = 'Failed to connect to Xtream server';
if (!error) {
return fallback;
}
const maybeError = error as {
message?: unknown;
statusText?: unknown;
error?: unknown;
};
if (typeof maybeError.message === 'string') {
if (maybeError.message.includes('[object Object]')) {
if (typeof maybeError.error === 'string') {
return maybeError.error;
}
if (
maybeError.error &&
typeof maybeError.error === 'object' &&
'message' in (maybeError.error as Record<string, unknown>) &&
typeof (maybeError.error as Record<string, unknown>).message ===
'string'
) {
return (maybeError.error as Record<string, string>).message;
}
return fallback;
}
return maybeError.message;
}
if (typeof maybeError.statusText === 'string') {
return maybeError.statusText;
}
if (typeof error === 'string') {
return error;
}
return fallback;
}
async forwardStalkerRequest(payload: {
url: string;
params: Record<string, string>;
@@ -71,7 +71,7 @@ export class XtreamApiService {
{
username: credentials.username,
password: credentials.password,
action: 'get_account_info',
action: XtreamCodeActions.GetAccountInfo,
}
);
}
@@ -1,4 +1,5 @@
export enum XtreamCodeActions {
GetAccountInfo = 'get_account_info',
GetLiveStreams = 'get_live_streams',
GetLiveCategories = 'get_live_categories',
GetSeries = 'get_series',