feat: Enhance M3U8 ad detection with proxy URL unwrapping and segment-level analysis, and implement proxy fallback for HLS playlist fetching.

This commit is contained in:
kuekhaoyang
2026-02-16 10:31:09 +08:00
parent 9aafe20505
commit bb7bc3e75f
5 changed files with 265 additions and 260 deletions
+17 -6
View File
@@ -201,6 +201,20 @@ export function useHlsPlayer({
// If it's in sub-playlists, it might fail unless we parse and blob those too (complex).
if (isAdFilterEnabled) {
const fetchWithFallback = async (url: string): Promise<string> => {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.text();
} catch (e) {
console.warn(`[HLS Native] Fetch failed for ${url}, trying proxy...`, e);
const proxiedUrl = `/api/proxy?url=${encodeURIComponent(url)}`;
const res = await fetch(proxiedUrl);
if (!res.ok) throw new Error(`Proxy fetch failed: HTTP ${res.status}`);
return await res.text();
}
};
const processMasterPlaylist = async (masterSrc: string) => {
// Move blob tracking outside try to ensure cleanup on error
const createdBlobs: string[] = [];
@@ -214,8 +228,7 @@ export function useHlsPlayer({
}
try {
const response = await fetch(absoluteMasterSrc);
const masterContent = await response.text();
const masterContent = await fetchWithFallback(absoluteMasterSrc);
// If it's a simple playlist (no variants), just filter and play
if (!masterContent.includes('#EXT-X-STREAM-INF')) {
@@ -246,8 +259,7 @@ export function useHlsPlayer({
if (isRelative || uri.startsWith('http')) {
try {
const absoluteUrl = isRelative ? new URL(uri, absoluteMasterSrc).toString() : uri;
const subRes = await fetch(absoluteUrl);
const subContent = await subRes.text();
const subContent = await fetchWithFallback(absoluteUrl);
const filteredSub = filterM3u8Ad(subContent, absoluteUrl, adFilterMode, adKeywords);
const subBlob = new Blob([filteredSub], { type: 'application/vnd.apple.mpegurl' });
const subBlobUrl = URL.createObjectURL(subBlob);
@@ -270,8 +282,7 @@ export function useHlsPlayer({
if (isRelative || trimmedLine.startsWith('http')) {
try {
const absoluteUrl = isRelative ? new URL(trimmedLine, absoluteMasterSrc).toString() : trimmedLine;
const subRes = await fetch(absoluteUrl);
const subContent = await subRes.text();
const subContent = await fetchWithFallback(absoluteUrl);
const filteredSub = filterM3u8Ad(subContent, absoluteUrl, adFilterMode, adKeywords);
const subBlob = new Blob([filteredSub], { type: 'application/vnd.apple.mpegurl' });
const subBlobUrl = URL.createObjectURL(subBlob);