From 09b8752a3db68314a2cf48d02734bb9b397afe71 Mon Sep 17 00:00:00 2001 From: 4gray Date: Mon, 12 Jan 2026 00:50:23 +0100 Subject: [PATCH] fix(xtream): switch to XtreamApiService and sendRequest --- apps/web/src/app/services/electron.service.ts | 19 +------ apps/web/src/app/services/pwa.service.ts | 4 -- .../account-info/account-info.component.ts | 19 +++---- .../services/xtream-api.service.ts | 49 +++++++++++++------ .../app/xtream-tauri/stores/xtream.store.ts | 39 +++++++-------- libs/services/src/lib/data.service.ts | 1 - 6 files changed, 60 insertions(+), 71 deletions(-) diff --git a/apps/web/src/app/services/electron.service.ts b/apps/web/src/app/services/electron.service.ts index 730bba602..976dcc7a3 100644 --- a/apps/web/src/app/services/electron.service.ts +++ b/apps/web/src/app/services/electron.service.ts @@ -12,6 +12,7 @@ import { PLAYLIST_PARSE_BY_URL, PLAYLIST_UPDATE, XTREAM_RESPONSE, + XTREAM_REQUEST, } from 'shared-interfaces'; import { AppConfig } from '../../environments/environment'; @@ -69,7 +70,7 @@ export class ElectronService extends DataService { title: string; } ); - } else if (type === 'XTREAM_REQUEST') { + } else if (type === XTREAM_REQUEST) { return await this.forwardXtreamRequest( payload as { url: string; params: Record } ); @@ -322,20 +323,4 @@ export class ElectronService extends DataService { getAppEnvironment(): string { return 'electron'; } - - async fetchData(url: string, queryParams: Params) { - const urlObject = new URL(url); - Object.entries(queryParams).forEach(([key, value]) => { - urlObject.searchParams.append(key, value); - }); - const response = await fetch(urlObject.toString()); - if (!response.ok) { - throw new Error( - `Error: ${response.statusText} (Status: ${response.status})` - ); - } - - const result = await response.json(); - return result; - } } diff --git a/apps/web/src/app/services/pwa.service.ts b/apps/web/src/app/services/pwa.service.ts index 643246807..3f4c4d040 100644 --- a/apps/web/src/app/services/pwa.service.ts +++ b/apps/web/src/app/services/pwa.service.ts @@ -285,8 +285,4 @@ export class PwaService extends DataService { getAppEnvironment(): string { return 'pwa'; } - - fetchData(url: string, queryParams: Params) { - // not implemented - } } diff --git a/apps/web/src/app/xtream-tauri/account-info/account-info.component.ts b/apps/web/src/app/xtream-tauri/account-info/account-info.component.ts index 2e1f168c3..94cae7146 100644 --- a/apps/web/src/app/xtream-tauri/account-info/account-info.component.ts +++ b/apps/web/src/app/xtream-tauri/account-info/account-info.component.ts @@ -5,7 +5,7 @@ import { MatListModule } from '@angular/material/list'; import { Store } from '@ngrx/store'; import { TranslatePipe } from '@ngx-translate/core'; import { selectActivePlaylist } from 'm3u-state'; -import { DataService } from 'services'; +import { XtreamApiService } from '../services/xtream-api.service'; import { XtreamAccountInfo } from './account-info.interface'; @Component({ @@ -20,7 +20,7 @@ export class AccountInfoComponent { liveStreamsCount: number; seriesCount: number; }>(MAT_DIALOG_DATA); - private readonly dataService = inject(DataService); + private readonly xtreamApiService = inject(XtreamApiService); private readonly store = inject(Store); accountInfo: XtreamAccountInfo; @@ -44,14 +44,11 @@ export class AccountInfoComponent { if (!playlist) return; try { - this.accountInfo = await this.dataService.fetchData( - `${playlist.serverUrl}/player_api.php`, - { - username: playlist.username, - password: playlist.password, - action: 'get_account_info', - } - ); + this.accountInfo = await this.xtreamApiService.getAccountInfo({ + serverUrl: playlist.serverUrl, + username: playlist.username, + password: playlist.password, + }); if (this.accountInfo) { this.formattedExpDate = new Date( @@ -73,4 +70,4 @@ export class AccountInfoComponent { get isTrial(): boolean { return this.accountInfo?.user_info?.is_trial === '1'; } -} +} \ No newline at end of file diff --git a/apps/web/src/app/xtream-tauri/services/xtream-api.service.ts b/apps/web/src/app/xtream-tauri/services/xtream-api.service.ts index 0c5a0bd14..b2f5274c9 100644 --- a/apps/web/src/app/xtream-tauri/services/xtream-api.service.ts +++ b/apps/web/src/app/xtream-tauri/services/xtream-api.service.ts @@ -9,6 +9,7 @@ import { XtreamSerieItem, XtreamVodDetails, XtreamVodStream, + XTREAM_REQUEST, } from 'shared-interfaces'; import { XtreamAccountInfo } from '../account-info/account-info.interface'; @@ -65,8 +66,8 @@ export class XtreamApiService { async getAccountInfo( credentials: XtreamCredentials ): Promise { - return this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + return this.sendRequest( + credentials.serverUrl, { username: credentials.username, password: credentials.password, @@ -88,8 +89,8 @@ export class XtreamApiService { series: XtreamCodeActions.GetSeriesCategories, }; - const response = await this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + const response = await this.sendRequest( + credentials.serverUrl, { action: actionMap[type], username: credentials.username, @@ -106,8 +107,8 @@ export class XtreamApiService { async getLiveStreams( credentials: XtreamCredentials ): Promise { - const response = await this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + const response = await this.sendRequest( + credentials.serverUrl, { action: XtreamCodeActions.GetLiveStreams, username: credentials.username, @@ -124,8 +125,8 @@ export class XtreamApiService { async getVodStreams( credentials: XtreamCredentials ): Promise { - const response = await this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + const response = await this.sendRequest( + credentials.serverUrl, { action: XtreamCodeActions.GetVodStreams, username: credentials.username, @@ -142,8 +143,8 @@ export class XtreamApiService { async getSeriesStreams( credentials: XtreamCredentials ): Promise { - const response = await this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + const response = await this.sendRequest( + credentials.serverUrl, { action: XtreamCodeActions.GetSeries, username: credentials.username, @@ -178,8 +179,8 @@ export class XtreamApiService { credentials: XtreamCredentials, vodId: string | number ): Promise { - return this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + return this.sendRequest( + credentials.serverUrl, { action: XtreamCodeActions.GetVodInfo, username: credentials.username, @@ -196,8 +197,8 @@ export class XtreamApiService { credentials: XtreamCredentials, seriesId: string | number ): Promise { - return this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + return this.sendRequest( + credentials.serverUrl, { action: XtreamCodeActions.GetSeriesInfo, username: credentials.username, @@ -216,8 +217,8 @@ export class XtreamApiService { streamId: number, limit: number = 10 ): Promise { - const response: EpgResponse = await this.dataService.fetchData( - `${credentials.serverUrl}/player_api.php`, + const response: EpgResponse = await this.sendRequest( + credentials.serverUrl, { action: XtreamCodeActions.GetShortEpg, username: credentials.username, @@ -256,4 +257,20 @@ export class XtreamApiService { return str; } } + + /** + * Send request via IPC to avoid CORS issues + */ + private async sendRequest(url: string, params: Record): Promise { + const serializedParams: Record = {}; + Object.entries(params).forEach(([key, value]) => { + serializedParams[key] = String(value); + }); + + const response = await this.dataService.sendIpcEvent(XTREAM_REQUEST, { + url, + params: serializedParams, + }); + return response?.payload; + } } diff --git a/apps/web/src/app/xtream-tauri/stores/xtream.store.ts b/apps/web/src/app/xtream-tauri/stores/xtream.store.ts index 25ed8d442..8f380482f 100644 --- a/apps/web/src/app/xtream-tauri/stores/xtream.store.ts +++ b/apps/web/src/app/xtream-tauri/stores/xtream.store.ts @@ -6,9 +6,7 @@ import { } from '@ngrx/signals'; import { Store } from '@ngrx/store'; import { selectActivePlaylist } from 'm3u-state'; -import { DataService } from 'services'; import { - XtreamCodeActions, XtreamSerieDetails, XtreamVodDetails, } from 'shared-interfaces'; @@ -17,6 +15,9 @@ import { import { withFavorites } from '../with-favorites.feature'; import { withRecentItems } from '../with-recent-items'; +// Import service +import { XtreamApiService } from '../services/xtream-api.service'; + // Import new feature stores import { withPortal, @@ -75,8 +76,10 @@ export const XtreamStore = signalStore( // Cross-feature methods & orchestration withMethods((store) => { - const dataService = inject(DataService); + const xtreamApiService = inject(XtreamApiService); const ngrxStore = inject(Store); + const searchContent = (store as any) + .searchContent as (term: string, types: string[]) => Promise; return { /** @@ -122,15 +125,11 @@ export const XtreamStore = signalStore( if (!playlist) return; store.setIsLoadingDetails(true); - dataService.fetchData( - `${playlist.serverUrl}/player_api.php`, - { - action: XtreamCodeActions.GetVodInfo, - username: playlist.username, - password: playlist.password, - vod_id: params.vodId, - } - ).then((vodDetails: XtreamVodDetails) => { + xtreamApiService.getVodInfo({ + serverUrl: playlist.serverUrl, + username: playlist.username, + password: playlist.password, + }, params.vodId).then((vodDetails: XtreamVodDetails) => { store.setSelectedCategory(params.categoryId); store.setSelectedItem({ ...vodDetails, @@ -154,15 +153,11 @@ export const XtreamStore = signalStore( if (!playlist) return; store.setIsLoadingDetails(true); - dataService.fetchData( - `${playlist.serverUrl}/player_api.php`, - { - action: XtreamCodeActions.GetSeriesInfo, - username: playlist.username, - password: playlist.password, - series_id: params.serialId, - } - ).then((serialDetails: XtreamSerieDetails) => { + xtreamApiService.getSeriesInfo({ + serverUrl: playlist.serverUrl, + username: playlist.username, + password: playlist.password, + }, params.serialId).then((serialDetails: XtreamSerieDetails) => { store.setSelectedCategory(params.categoryId); store.setSelectedItem({ ...serialDetails, @@ -221,7 +216,7 @@ export const XtreamStore = signalStore( */ searchContent(params: { term: string; types: string[] }): void { // Call the underlying search method from withSearch - (store as any).searchContent(params.term, params.types); + void searchContent(params.term, params.types); }, }; }) diff --git a/libs/services/src/lib/data.service.ts b/libs/services/src/lib/data.service.ts index 17aff072e..af3b73113 100644 --- a/libs/services/src/lib/data.service.ts +++ b/libs/services/src/lib/data.service.ts @@ -17,7 +17,6 @@ export abstract class DataService { abstract getAppVersion(): string; abstract sendIpcEvent(type: string, payload?: unknown): any; abstract removeAllListeners(type: string): void; - abstract fetchData(url: string, queryParams: Params): any; abstract listenOn( command: string, callback: (...args: any[]) => void