From d663dff0d6c2ca732a7899e2a1ad9f88194158bf Mon Sep 17 00:00:00 2001 From: Md Tahseen Hussain Date: Fri, 3 Apr 2026 19:52:19 +0530 Subject: [PATCH] Improve API error handling and refresh README response examples --- README.md | 42 +++++++++++++++++------------------ index.js | 18 ++++++++++----- lib/animepahe.js | 58 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 87 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 2d2566c..0d8d6f7 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Output: --- ```http -GET /episodes?session=27a95751-0311-47ed-dbce-7f0680d5074a +GET /episodes?session=77bbe16e-fd87-13d9-a18c-4edb06884a33 ``` Output: @@ -82,18 +82,18 @@ Output: ```json [ { - "id": 70730, + "id": 15779, "number": 1, "title": "Episode 1", - "snapshot": "https://i.animepahe.si/uploads/snapshots/22c034f704a286b5ce17cc33a3dccf9258cc83038e5bafbcc5a196b2584c3454.jpg", - "session": "800a1f7d29d6ebb94d2bfd320b2001b95d00decff4aaecaa6fbef5916379a762" + "snapshot": "https://i.animepahe.com/snapshots/6693c23144b7f3d2bc73242740527ea77baa1d2649180bdb63f35b6a0ef9188b.jpg", + "session": "18ea551da39ccf31e77f9702365193b45636c5ffe7168f209830a56607f2a9d3" }, { - "id": 70823, + "id": 15780, "number": 2, "title": "Episode 2", - "snapshot": "https://i.animepahe.si/uploads/snapshots/baf28a9ea1fecf9bbee49844cf3b782632e487ff49d3ba5c93b56241719fab05.jpg", - "session": "4dd535ded2d2773bb3285881839d018c5787619de262dffb801ab4f78cf20123" + "snapshot": "https://i.animepahe.com/snapshots/44025fa8399d9d68584903c2b5b23c579fd1d73f33570d2d95e783895d97f757.jpg", + "session": "38517b2d536b4c2a2c0c082f1995eb26b04c8af98889afebdb26f344a33c324b" } ] ``` @@ -101,7 +101,7 @@ Output: --- ```http -GET /sources?anime_session=27a95751-0311-47ed-dbce-7f0680d5074a&episode_session=800a1f7d29d6ebb94d2bfd320b2001b95d00decff4aaecaa6fbef5916379a762 +GET /sources?anime_session=77bbe16e-fd87-13d9-a18c-4edb06884a33&episode_session=18ea551da39ccf31e77f9702365193b45636c5ffe7168f209830a56607f2a9d3 ``` Output: @@ -109,21 +109,15 @@ Output: ```json [ { - "url": "https://kwik.si/e/KcfYGhr86Ww2", - "quality": "1080p", - "fansub": "KawaSubs", + "url": "https://kwik.cx/e/4q3V1NDdRVy9", + "quality": "800p", + "fansub": "df68", "audio": "jpn" }, { - "url": "https://kwik.si/e/Sr2gRRoVz6wy", - "quality": "720p", - "fansub": "KawaSubs", - "audio": "jpn" - }, - { - "url": "https://kwik.si/e/J7jBHBSJhTEv", + "url": "https://kwik.cx/e/GDFF1EyEUCLD", "quality": "360p", - "fansub": "KawaSubs", + "fansub": "df68", "audio": "jpn" } ] @@ -132,14 +126,20 @@ Output: --- ```http -GET /m3u8?url=https://kwik.si/e/uEPQKLMzFpaz +GET /m3u8?url=https://kwik.cx/e/4q3V1NDdRVy9 ``` Output: ```json { - "m3u8": "https://vault-12.owocdn.top/stream/12/12/1478df0e98f767de547ac36d33bc92b73b9a5b7318fe3f3e81328fa31fc1eac3/uwu.m3u8" + "m3u8": "https://vault-01.uwucdn.top/stream/01/03/b92a392054c041a3f9c6eecabeb0e127183f44e547828447b10bca8d77523e6f/uwu.m3u8", + "referer": "https://kwik.cx/e/4q3V1NDdRVy9", + "headers": { + "Referer": "https://kwik.cx/e/4q3V1NDdRVy9", + "Origin": "https://kwik.cx" + }, + "proxy_url": "/proxy?url=https%3A%2F%2Fvault-01.uwucdn.top%2Fstream%2F01%2F03%2Fb92a392054c041a3f9c6eecabeb0e127183f44e547828447b10bca8d77523e6f%2Fuwu.m3u8&referer=https%3A%2F%2Fkwik.cx%2Fe%2F4q3V1NDdRVy9" } ``` diff --git a/index.js b/index.js index 433e4d6..06f5838 100644 --- a/index.js +++ b/index.js @@ -15,6 +15,14 @@ app.use(express.static('public')); // Create AnimePahe instance const pahe = new AnimePahe(); +function mapErrorToStatusCode(message) { + const text = String(message || '').toLowerCase(); + if (text.includes('not found')) return 404; + if (text.includes('blocked') || text.includes('anti-bot')) return 503; + if (text.includes('forbidden')) return 403; + return 500; +} + // Routes app.get('/', (req, res) => { res.json({ @@ -51,7 +59,7 @@ app.get('/search', async (req, res) => { res.json(results); } catch (error) { console.error('Search error:', error); - res.status(500).json({ error: error.message }); + res.status(mapErrorToStatusCode(error.message)).json({ error: error.message }); } }); @@ -65,7 +73,7 @@ app.get('/episodes', async (req, res) => { res.json(episodes); } catch (error) { console.error('Episodes error:', error); - res.status(500).json({ error: error.message }); + res.status(mapErrorToStatusCode(error.message)).json({ error: error.message }); } }); @@ -81,7 +89,7 @@ app.get('/sources', async (req, res) => { res.json(sources); } catch (error) { console.error('Sources error:', error); - res.status(500).json({ error: error.message }); + res.status(mapErrorToStatusCode(error.message)).json({ error: error.message }); } }); @@ -95,7 +103,7 @@ app.get('/ids', async (req, res) => { res.json(ids); } catch (error) { console.error('IDs error:', error); - res.status(500).json({ error: error.message }); + res.status(mapErrorToStatusCode(error.message)).json({ error: error.message }); } }); @@ -119,7 +127,7 @@ app.get('/m3u8', async (req, res) => { }); } catch (error) { console.error('M3U8 resolution error:', error); - res.status(500).json({ error: error.message }); + res.status(mapErrorToStatusCode(error.message)).json({ error: error.message }); } }); diff --git a/lib/animepahe.js b/lib/animepahe.js index 4a1a28d..3a67ea9 100644 --- a/lib/animepahe.js +++ b/lib/animepahe.js @@ -104,6 +104,54 @@ class AnimePahe { return animeSession; } + /** + * Convert noisy upstream errors into concise API-safe messages + * @param {string} context - Operation context (search, episodes, etc) + * @param {Error|any} error - Raw error + * @returns {string} Public error message + * @private + */ + _formatUpstreamError(context, error) { + const rawMessage = String(error?.message || error || 'Unknown error'); + const statusMatch = rawMessage.match(/^(\d{3})\s*-\s*/); + const statusCode = statusMatch ? parseInt(statusMatch[1], 10) : null; + + if (statusCode === 404 || /Oops\.\.\.\s*404|404\s+Not\s+Found/i.test(rawMessage)) { + if (context === 'episodes') { + return 'Anime session not found. Use /search first to get a valid session id.'; + } + if (context === 'sources') { + return 'Anime or episode session not found. Use /episodes first to get a valid episode_session.'; + } + if (context === 'ids') { + return 'Anime session not found. Use /search first to get a valid session id.'; + } + } + + if (statusCode === 403 && /ddos-guard|checking your browser|cloudflare/i.test(rawMessage)) { + return 'Upstream blocked the request (anti-bot challenge). Please retry shortly.'; + } + + let cleaned = rawMessage + .replace(//gi, ' ') + .replace(//gi, ' ') + .replace(/<[^>]+>/g, ' ') + .replace(/\s+/g, ' ') + .replace(/^\d{3}\s*-\s*"?/, '') + .replace(/"$/, '') + .trim(); + + if (!cleaned) { + cleaned = 'Unexpected upstream error'; + } + + if (cleaned.length > 220) { + cleaned = `${cleaned.slice(0, 220)}...`; + } + + return cleaned; + } + /** * Search for anime by query * @param {string} query - Search query @@ -138,7 +186,7 @@ class AnimePahe { return results; } catch (error) { - throw new Error(`Search failed: ${error.message}`); + throw new Error(`Search failed: ${this._formatUpstreamError('search', error)}`); } } @@ -208,7 +256,7 @@ class AnimePahe { return formattedEpisodes; } catch (error) { - throw new Error(`Failed to get episodes: ${error.message}`); + throw new Error(`Failed to get episodes: ${this._formatUpstreamError('episodes', error)}`); } } @@ -288,7 +336,7 @@ class AnimePahe { return uniqueSources; } catch (error) { - throw new Error(`Failed to get sources: ${error.message}`); + throw new Error(`Failed to get sources: ${this._formatUpstreamError('sources', error)}`); } } @@ -394,7 +442,7 @@ ${transformedScript} throw new Error(`Could not resolve .m3u8. Node output (first 2000 chars):\n${nodeOutput.substring(0, 2000)}`); } catch (error) { - throw new Error(`Failed to resolve Kwik URL: ${error.message}`); + throw new Error(`Failed to resolve Kwik URL: ${this._formatUpstreamError('m3u8', error)}`); } } @@ -419,7 +467,7 @@ ${transformedScript} myanimelist: malId ? parseInt(malId, 10) : null }; } catch (error) { - throw new Error(`Failed to get IDs: ${error.message}`); + throw new Error(`Failed to get IDs: ${this._formatUpstreamError('ids', error)}`); } }