diff --git a/apps/web/src/app/services/electron.service.ts b/apps/web/src/app/services/electron.service.ts index cb3544641..4d98bea9c 100644 --- a/apps/web/src/app/services/electron.service.ts +++ b/apps/web/src/app/services/electron.service.ts @@ -216,14 +216,52 @@ export class ElectronService extends DataService { } private async fetchM3uPlaylistFromUrl(payload: Partial) { - window.electron.fetchPlaylistByUrl(payload.url).then((result) => { - this.store.dispatch( - PlaylistActions.handleAddingPlaylistByUrl({ - isTemporary: !!payload?.isTemporary, - playlist: result, - }) - ); - }); + window.electron + .fetchPlaylistByUrl(payload.url) + .then((result) => { + this.store.dispatch( + PlaylistActions.handleAddingPlaylistByUrl({ + isTemporary: !!payload?.isTemporary, + playlist: result, + }) + ); + }) + .catch((error: unknown) => { + const statusCode = this.extractHttpStatusCode(error); + let messageKey = + 'HOME.URL_UPLOAD.ERROR_FETCH_FAILED'; + if (statusCode === 403) { + messageKey = 'HOME.URL_UPLOAD.ERROR_403'; + } else if (statusCode === 404) { + messageKey = 'HOME.URL_UPLOAD.ERROR_404'; + } else if (statusCode === 401) { + messageKey = 'HOME.URL_UPLOAD.ERROR_401'; + } + this.snackBar.open( + this.translateService.instant(messageKey), + this.translateService.instant('CLOSE'), + { duration: 5000 } + ); + }); + } + + private extractHttpStatusCode(error: unknown): number | null { + if ( + error && + typeof error === 'object' && + 'response' in error && + error.response && + typeof error.response === 'object' && + 'status' in error.response + ) { + return error.response.status as number; + } + // Parse status from error message string (IPC serialization) + const msg = String( + (error as { message?: string })?.message ?? error + ); + const match = msg.match(/status code (\d{3})/); + return match ? parseInt(match[1], 10) : null; } private async updateM3uPlaylistFromFile(data: { diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index 0435ae86c..ca9faff95 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -89,7 +89,11 @@ "URL_UPLOAD": { "PLAYLIST_URL": "Playlist URL (m3u, m3u8)", "ADD_PLAYLIST": "Add playlist", - "CORS_NOTE": "Note: In order to avoid CORS issues at this point the application uses a public CORS Proxy service. If you want to upload a playlist with sensitive data, it's more secure to import it from a file." + "CORS_NOTE": "Note: In order to avoid CORS issues at this point the application uses a public CORS Proxy service. If you want to upload a playlist with sensitive data, it's more secure to import it from a file.", + "ERROR_FETCH_FAILED": "Failed to fetch the playlist. Please check the URL and try again.", + "ERROR_403": "Access denied (403): The server refused the request. The URL may require authentication or the playlist is restricted.", + "ERROR_404": "Playlist not found (404): The URL does not point to a valid playlist. Please check the URL.", + "ERROR_401": "Unauthorized (401): Authentication is required to access this playlist." }, "TEXT_IMPORT": { "LABEL": "Insert m3u(8) playlist as text",