From 3d73668b39972e73f6a03dec57d963b03363c691 Mon Sep 17 00:00:00 2001 From: kuekhaoyang Date: Sun, 23 Nov 2025 18:15:15 +0800 Subject: [PATCH] feat: Update service worker cache, add a utility page to clear service workers and caches, enhance proxy request headers and error handling, and improve HLS manifest parsing for absolute URLs. --- app/api/proxy/route.ts | 58 ++++++++++++++++++++++++++++++++-- lib/utils/hlsManifestParser.ts | 14 ++++++-- public/clear-sw.html | 39 +++++++++++++++++++++++ public/sw.js | 7 +++- 4 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 public/clear-sw.html diff --git a/app/api/proxy/route.ts b/app/api/proxy/route.ts index b8a9939..3f7d0e7 100644 --- a/app/api/proxy/route.ts +++ b/app/api/proxy/route.ts @@ -10,16 +10,47 @@ export async function GET(request: NextRequest) { try { // Beijing IP address to simulate request from China const chinaIP = '202.108.22.5'; + const urlObj = new URL(url); const response = await fetch(url, { headers: { - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + // User agent + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + + // IP-related headers (multiple formats for better compatibility) 'X-Forwarded-For': chinaIP, + 'X-Real-IP': chinaIP, 'Client-IP': chinaIP, - 'Referer': new URL(url).origin, + 'True-Client-IP': chinaIP, + + // Geographic/location headers + 'CF-IPCountry': 'CN', // Cloudflare-style country code + 'X-Country-Code': 'CN', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + + // Standard browser headers + 'Accept': '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'Cache-Control': 'no-cache', + 'Pragma': 'no-cache', + + // Referrer headers + 'Referer': urlObj.origin, + 'Origin': urlObj.origin, + + // Connection + 'Connection': 'keep-alive', }, }); + // Log response status to help debug on Vercel + console.log(`Proxy request to ${url} - Status: ${response.status} ${response.statusText}`); + + // Log warning for non-successful responses + if (!response.ok) { + console.warn(`Non-OK response: ${response.status} for URL: ${url}`); + } + const contentType = response.headers.get('Content-Type'); // Handle m3u8 playlists: rewrite URLs to go through proxy @@ -70,7 +101,28 @@ export async function GET(request: NextRequest) { return newResponse; } catch (error) { console.error('Proxy error:', error); - return new NextResponse('Proxy failed', { status: 500 }); + console.error('Failed URL:', url); + + // Log detailed error information to help debug on Vercel + if (error instanceof Error) { + console.error('Error message:', error.message); + console.error('Error stack:', error.stack); + } + + return new NextResponse( + JSON.stringify({ + error: 'Proxy request failed', + message: error instanceof Error ? error.message : 'Unknown error', + url: url + }), + { + status: 500, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + } + } + ); } } diff --git a/lib/utils/hlsManifestParser.ts b/lib/utils/hlsManifestParser.ts index c0dfe39..dbd8e1f 100644 --- a/lib/utils/hlsManifestParser.ts +++ b/lib/utils/hlsManifestParser.ts @@ -28,9 +28,17 @@ export async function parseHLSManifest(src: string): Promise { const durationStr = trimmed.substring(8).split(',')[0]; currentSegmentDuration = parseFloat(durationStr); } else if (trimmed && !trimmed.startsWith('#')) { - // Use URL API to resolve relative paths correctly against the manifest URL - // This handles cases where baseUrl might end with / and segment starts with / - const segmentUrl = new URL(trimmed, src).toString(); + // Detect if the line is already an absolute URL (from proxy route) + // If so, use it directly without parsing + let segmentUrl: string; + if (trimmed.startsWith('http://') || trimmed.startsWith('https://') || trimmed.startsWith('/')) { + // Already absolute URL or absolute path - use as is + segmentUrl = trimmed; + } else { + // Relative URL - resolve against manifest URL + segmentUrl = new URL(trimmed, src).toString(); + } + segments.push({ url: segmentUrl, duration: currentSegmentDuration, diff --git a/public/clear-sw.html b/public/clear-sw.html new file mode 100644 index 0000000..114569a --- /dev/null +++ b/public/clear-sw.html @@ -0,0 +1,39 @@ + + + + Clear Service Worker + + +

清除 Service Worker

+ +
+ + + + diff --git a/public/sw.js b/public/sw.js index 27efb1c..4617c4f 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,4 +1,4 @@ -const CACHE_NAME = 'video-cache-v1'; +const CACHE_NAME = 'video-cache-v2'; self.addEventListener('install', (event) => { self.skipWaiting(); @@ -21,6 +21,11 @@ self.addEventListener('activate', (event) => { self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); + // Skip proxy API routes - they handle their own caching and URL rewriting + if (url.pathname.startsWith('/api/proxy')) { + return; // Let the request pass through without Service Worker intervention + } + // Intercept HLS manifest files (.m3u8) if (url.pathname.endsWith('.m3u8')) { event.respondWith(