From 9b1ac11ff090e396631468c54e6e21e84e44a6d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=CA=9C=C9=AA=C9=B4=E1=B4=87=C9=AA=20N=E1=B4=8F=E1=B4=9C?=
=?UTF-8?q?=E1=B4=A2=E1=B4=87=C9=B4?=
<157171073+Shineii86@users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:43:11 +0530
Subject: [PATCH] feat: improve subtitle extraction, quality fallback, and
documentation
---
CHANGELOG.md | 20 ++++++++++++++++++++
README.md | 3 ++-
docs/endpoints.md | 28 +++++++++++++++++++++++++++-
package.json | 2 +-
public/openapi.json | 45 ++++++++++++++++++++++++++++++++++++++++++---
src/helpers/pipe.js | 42 ++++++++++++++++++++++++++++++------------
test.js | 4 ++--
7 files changed, 124 insertions(+), 20 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7621ebc..3ffd31a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## v2.0.1
+### Streaming Improvements — Better subtitle extraction, quality fallback
+
+#### Streaming Enhancements
+- Improved `extractSubtitles` — now handles multiple provider formats (bonk: file/label/kind/language, others: url/name/lang)
+- Updated `getBestStream` — falls back to first active HLS stream when quality metadata is missing
+- Updated `getDownloadUrl` — now returns subtitles and bestStream in response
+- Added subtitle fields: url, label, language, kind, format, encoding, isDefault
+
+#### Provider Improvements
+- Bonk provider confirmed to return subtitles (English, VTT format, captions kind)
+- Better handling of providers without quality metadata (bonk, ally, bee return undefined quality)
+
+#### Verified
+- Subtitle extraction working: bonk provider returns 1 English subtitle per episode
+- Download URL working: bonk provider returns direct download link
+- Batch sources: subtitles included in batch responses
+
+---
+
## v2.0.0
### Major Release — 35 endpoints, streaming improvements, new features
diff --git a/README.md b/README.md
index 81d046c..f0a4f07 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
-
+
@@ -1408,6 +1408,7 @@ docker run -p 3000:3000 miruroapi
| Version | Date | Key Changes |
|:---|:---|:---|
+| **2.0.1** | 2026-06-24 | Streaming improvements — better subtitle extraction, quality fallback for providers without metadata |
| **2.0.0** | 2026-06-25 | Major upgrade — 35 endpoints, compression, streaming improvements, new features |
| **1.4.0** | 2026-06-25 | Creator attribution middleware, Vercel deployment fix, security headers |
| **1.3.0** | 2026-06-25 | Full landing page upgrade with premium design |
diff --git a/docs/endpoints.md b/docs/endpoints.md
index a14e6aa..14f3dfb 100644
--- a/docs/endpoints.md
+++ b/docs/endpoints.md
@@ -1,6 +1,6 @@
# API Endpoints Reference
-Complete documentation for all 35 MiruroAPI v2.0.0 endpoints.
+Complete documentation for all 35 MiruroAPI v2.0.1 endpoints.
---
@@ -498,6 +498,32 @@ Streaming sources via URL path (recommended).
| category | string | sub or dub |
| slug | string | Episode slug |
+### Response Fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| streams | array | Array of stream objects (hls/embed) |
+| download | string | Direct download URL (if available) |
+| subtitles | array | Subtitle objects (if available) |
+| bestStream | object | Best available HLS stream with quality fallback |
+
+### Subtitle Object
+
+| Field | Type | Description |
+|-------|------|-------------|
+| url | string | VTT subtitle file URL |
+| label | string | Display name (e.g., "English") |
+| language | string | Language code (e.g., "en") |
+| kind | string | "subtitles" or "captions" |
+| format | string | "vtt" format |
+| encoding | string | "utf-8" |
+| isDefault | boolean | Is default subtitle |
+
+### Note
+- Bonk provider returns subtitles (English, VTT format)
+- Other providers may not return subtitles
+- Quality fallback: 1080p → 720p → 480p → 360p
+
---
## Compare
diff --git a/package.json b/package.json
index 1c94540..2f2e8e1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "miruro-api",
- "version": "2.0.0",
+ "version": "2.0.1",
"description": "RESTful API for miruro.tv - Anime streaming data powered by AniList GraphQL and Miruro streaming providers",
"main": "server.js",
"scripts": {
diff --git a/public/openapi.json b/public/openapi.json
index b39a75c..80d3cbb 100644
--- a/public/openapi.json
+++ b/public/openapi.json
@@ -3,7 +3,7 @@
"info": {
"title": "MiruroAPI",
"description": "Free RESTful API for anime streaming data. Powered by AniList GraphQL and Miruro streaming providers.",
- "version": "2.0.0",
+ "version": "2.0.1",
"contact": {
"name": "Shineii86",
"url": "https://github.com/Shineii86/MiruroAPI"
@@ -339,13 +339,52 @@
"/api/watch/{provider}/{anilistId}/{category}/{slug}": {
"get": {
"tags": ["Streaming"],
- "summary": "Streaming sources (simple)",
+ "summary": "Streaming sources (simple) with subtitles",
+ "description": "Returns streaming sources, subtitles (if available), and best stream with quality fallback",
"parameters": [
{ "name": "provider", "in": "path", "required": true, "schema": { "type": "string" } },
{ "name": "anilistId", "in": "path", "required": true, "schema": { "type": "integer" } },
{ "name": "category", "in": "path", "required": true, "schema": { "type": "string" } },
{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }
- ]
+ ],
+ "responses": {
+ "200": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "success": { "type": "boolean" },
+ "results": {
+ "type": "object",
+ "properties": {
+ "streams": { "type": "array" },
+ "download": { "type": "string" },
+ "subtitles": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "url": { "type": "string", "description": "VTT subtitle file URL" },
+ "label": { "type": "string", "description": "Display name" },
+ "language": { "type": "string", "description": "Language code" },
+ "kind": { "type": "string", "description": "subtitles or captions" },
+ "format": { "type": "string", "description": "vtt format" },
+ "encoding": { "type": "string", "description": "utf-8" },
+ "isDefault": { "type": "boolean", "description": "Is default subtitle" }
+ }
+ }
+ },
+ "bestStream": { "type": "object" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
},
"/api/compare/{id1}/{id2}": {
diff --git a/src/helpers/pipe.js b/src/helpers/pipe.js
index 81384b2..9d5d78b 100644
--- a/src/helpers/pipe.js
+++ b/src/helpers/pipe.js
@@ -327,19 +327,27 @@ const getWatchSources = async (provider, anilistId, category, slug) => {
// ---- FEATURE: Extract subtitle URLs from streaming sources ----
/**
* Extracts and formats subtitle URLs from the pipe sources response.
- * Subtitles are usually in WebVTT format with language tags.
+ * Handles multiple subtitle formats from different providers.
+ *
+ * Provider formats:
+ * bonk: { file, label, kind, default, language, format, encoding }
+ * others: { url, name, lang, format }
*
* @param {object} sources - The sources response from getSources/getWatchSources
- * @returns {Array} Array of subtitle objects { url, label, language, format }
+ * @returns {Array} Array of subtitle objects { url, label, language, kind, format, isDefault }
*/
const extractSubtitles = (sources) => {
- const subtitles = sources.subtitles || sources.captions || [];
- return subtitles.map((sub) => ({
- url: sub.url || sub.file,
- label: sub.label || sub.name || "Unknown",
- language: sub.lang || sub.language || sub.label || "en",
+ const raw = sources.subtitles || sources.captions || [];
+
+ return raw.map((sub) => ({
+ url: sub.file || sub.url || null,
+ label: sub.label || sub.name || sub.language || "Unknown",
+ language: sub.language || sub.lang || sub.label || "en",
+ kind: sub.kind || "subtitles",
format: sub.format || "vtt",
- }));
+ encoding: sub.encoding || "utf-8",
+ isDefault: sub.default || false,
+ })).filter((sub) => sub.url);
};
// ══════════════════════════════════════════════════════════════
@@ -349,7 +357,8 @@ const extractSubtitles = (sources) => {
// ---- FEATURE: Get best available stream with quality fallback ----
/**
* Returns the best available HLS stream with automatic quality fallback.
- * Tries 1080p → 720p → 360p in order.
+ * Tries 1080p → 720p → 480p → 360p in order.
+ * Falls back to first HLS stream if no quality match.
*
* @param {object} sources - The sources response
* @param {string} [preferredQuality="1080p"] - Preferred quality
@@ -371,7 +380,10 @@ const getBestStream = (sources, preferredQuality = "1080p") => {
if (match) return match;
}
- // NOTE: Return first available if no quality match
+ // NOTE: Some providers don't include quality — return first active HLS stream, or first HLS stream
+ const active = streams.find((s) => s.isActive);
+ if (active) return active;
+
return streams[0];
};
@@ -382,23 +394,29 @@ const getBestStream = (sources, preferredQuality = "1080p") => {
// ---- FEATURE: Get download URL for an episode ----
/**
* Fetches the download URL for a specific episode from the pipe.
- * Returns the direct download link if available.
+ * Returns the direct download link, subtitles, and best stream if available.
*
* @param {string} provider - Provider name
* @param {number} anilistId - The AniList anime ID
* @param {string} category - Audio category ("sub" or "dub")
* @param {string} slug - Episode slug (e.g., "animepahe-1")
- * @returns {Promise