feat: Add streaming URL resolver, quality detection, and server normalization

New endpoints:
- GET /api/stream/resolve — Resolves actual m3u8/mp4 URLs from embed player URLs
  Follows chain: vidtube.site → megaplay API → m3u8 playlist
  Returns url, type, qualities, subtitles, skipData
- GET /api/stream/qualities — Parses M3U8 playlists for available quality options
  Returns sorted quality list with resolution, bandwidth, URLs

New extractors:
- streamResolver.extractor.js — Full stream resolution pipeline
  - resolveStreamUrl(): Resolves embed → actual video URL
  - parseM3u8Qualities(): Parses master playlists for qualities
  - normalizeServerName(): Maps VidPlay-1 → vidplay, HD-1 → hd
  - extractSubtitles(): Extracts subtitle tracks from API responses

Enhancements:
- streamInfo.extractor.js: Added session establishment before AJAX calls
- Server list now includes normalizedName field for each server
- Stream info now includes type (hls/mp4) and backup URL
- Server list includes typeLabel (SUB/HSUB/DUB)
- Session cookies properly maintained for AJAX stream endpoints

Architecture:
- Embed API domain mapping (vidtube.site → megaplay-1.buzz)
- M3U8 parser supports master and media playlists
- Subtitle extraction from multiple API response formats
- 36 total endpoints (was 34)
This commit is contained in:
Shinei Nouzen
2026-07-28 20:40:17 +05:30
parent e8c942a773
commit bdf7e96ce9
5 changed files with 513 additions and 24 deletions
+22 -1
View File
@@ -26,6 +26,7 @@ import { getSearchResults, getSearchSuggestions } from "../controllers/search.co
import { getEpisodeList } from "../controllers/episodeList.controller.js";
import { getEpisodeListAjax } from "../controllers/episodeListAjax.controller.js";
import { getStreamInfo, getServerList, getMapperServers } from "../controllers/streamInfo.controller.js";
import { getResolvedStream, getStreamQualities } from "../controllers/streamResolver.controller.js";
import { getSchedule } from "../controllers/schedule.controller.js";
import { getSpotlight } from "../controllers/spotlight.controller.js";
import { getTrending } from "../controllers/trending.controller.js";
@@ -209,6 +210,24 @@ app.use((req, res, next) => {
}
});
// ---- FEATURE: Resolve actual stream URL (m3u8/mp4) from link ID ----
app.get("/api/stream/resolve", async (req, res, next) => {
try {
await getResolvedStream(req, res, next);
} catch (error) {
jsonError(res, error.message);
}
});
// ---- FEATURE: Available quality options for a stream URL ----
app.get("/api/stream/qualities", async (req, res, next) => {
try {
await getStreamQualities(req, res, next);
} catch (error) {
jsonError(res, error.message);
}
});
// ══════════════════════════════════════════════════════════════
// SCHEDULE
// ══════════════════════════════════════════════════════════════
@@ -475,7 +494,7 @@ app.get("/api/stats", (req, res) => {
ttl: "5 minutes",
description: "Map-based cache with TTL expiration",
},
endpoints: 34,
endpoints: 36,
timestamp: new Date().toISOString(),
},
});
@@ -544,6 +563,8 @@ app.get("/api/openapi", (req, res) => {
"/mirrors/reset": { post: { summary: "Reset mirror cache", tags: ["System"] } },
"/openapi": { get: { summary: "OpenAPI spec", tags: ["System"] } },
"/watch": { get: { summary: "Watch page data", tags: ["Streaming"] } },
"/stream/resolve": { get: { summary: "Resolve actual stream URL (m3u8/mp4)", tags: ["Streaming"] } },
"/stream/qualities": { get: { summary: "Available quality options for M3U8 stream", tags: ["Streaming"] } },
"/search/suggest": { get: { summary: "Search suggestions", tags: ["Search"] } },
"/episodes-ajax/{id}": { get: { summary: "AJAX episode list", tags: ["Episodes"] } },
"/mapper-servers": { get: { summary: "Mapper servers", tags: ["Streaming"] } },