From 320bf0915b1857cab26648bd55670dc4bf993730 Mon Sep 17 00:00:00 2001 From: Shinei Nouzen <157171073+Shineii86@users.noreply.github.com> Date: Fri, 14 Aug 2026 06:36:37 +0530 Subject: [PATCH] fix: detect upstream API errors in stream endpoint to prevent null URL responses - Added upstream error detection in streamInfo.extractor.js (status >= 400) - Added non-JSON error string detection from upstream API - Updated streamInfo.controller.js to return HTTP 502 for upstream failures - Fixes /api/stream returning null URL when upstream returns error responses --- CHANGELOG.md | 10 ++++++++++ src/controllers/streamInfo.controller.js | 7 ++++++- src/extractors/streamInfo.extractor.js | 24 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15f2800..0f3808d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.2] - 2026-08-14 + +### Fixed +- **CRITICAL**: Fixed `/api/stream` returning `null` URL when upstream API returns error responses (e.g. `{status: 500, result: "Bad request"}`) — extractor now detects upstream error status codes and throws meaningful errors instead of silently returning null +- Fixed `streamInfo.extractor.js` `extractStreamInfo` to detect upstream API error responses (`data.status >= 400`) and throw descriptive errors +- Fixed `streamInfo.extractor.js` `extractStreamInfo` to detect non-JSON error strings from upstream API (e.g. "Bad request") and throw errors instead of silently swallowing them +- Fixed `streamInfo.extractor.js` `extractServerList` to detect upstream API error responses and throw descriptive errors +- Fixed `streamInfo.controller.js` `getStreamInfo` to return HTTP 502 (Bad Gateway) with error message for upstream API failures instead of silently returning null URL +- Fixed `streamInfo.controller.js` `getServerList` to properly propagate upstream errors via error handler + ## [2.2.1] - 2026-08-12 ### Fixed diff --git a/src/controllers/streamInfo.controller.js b/src/controllers/streamInfo.controller.js index 3889e0a..e43dafa 100644 --- a/src/controllers/streamInfo.controller.js +++ b/src/controllers/streamInfo.controller.js @@ -53,7 +53,12 @@ const getStreamInfo = async (req, res, next) => { if (cached) { return res.json({ success: true, results: cached }); } - const data = await extractStreamInfo(id); + let data; + try { + data = await extractStreamInfo(id); + } catch (upstreamError) { + return res.status(502).json({ success: false, message: `Upstream API error: ${upstreamError.message}` }); + } if (!data.url) { return res.status(404).json({ success: false, message: "Stream URL not found for this link ID", results: data }); } diff --git a/src/extractors/streamInfo.extractor.js b/src/extractors/streamInfo.extractor.js index e9f2e62..aeb2207 100644 --- a/src/extractors/streamInfo.extractor.js +++ b/src/extractors/streamInfo.extractor.js @@ -59,11 +59,27 @@ const extractStreamInfo = async (linkId, watchSlug = null) => { try { data = JSON.parse(raw); } catch { data = {}; } } + // NOTE: Detect upstream API error responses (e.g. {status: 500, result: "Bad request"}) + if (data && typeof data === "object" && data.status && Number(data.status) >= 400) { + throw new Error(`Upstream API error (${data.status}): ${data.result || "Unknown error"}`); + } + if (!data || !data.result) { return { linkId, url: null, type: null, skipData: null }; } - const result = typeof data.result === "string" ? (() => { try { return JSON.parse(data.result); } catch { return {}; } })() : data.result; + // NOTE: If result is a non-JSON string (error message), treat as upstream failure + const result = typeof data.result === "string" + ? (() => { + try { return JSON.parse(data.result); } + catch { + if (!data.result.startsWith("{") && !data.result.startsWith("[")) { + throw new Error(`Upstream returned invalid data: ${data.result}`); + } + return {}; + } + })() + : data.result; const streamUrl = result.url || null; const streamType = streamUrl?.includes(".m3u8") ? "hls" : streamUrl ? "mp4" : null; @@ -115,6 +131,12 @@ const extractServerList = async (episodeIds, watchSlug = null) => { if (typeof raw === "string") { try { parsed = JSON.parse(raw); } catch { parsed = {}; } } + + // NOTE: Detect upstream API error responses + if (parsed && typeof parsed === "object" && parsed.status && Number(parsed.status) >= 400) { + throw new Error(`Upstream API error (${parsed.status}): ${parsed.result || "Unknown error"}`); + } + const html = parsed?.result || ""; const $ = cheerio.load(html);