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.

This commit is contained in:
kuekhaoyang
2025-11-23 18:15:15 +08:00
parent 7ec01a73d6
commit 3d73668b39
4 changed files with 111 additions and 7 deletions
+55 -3
View File
@@ -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': '*',
}
}
);
}
}
+11 -3
View File
@@ -28,9 +28,17 @@ export async function parseHLSManifest(src: string): Promise<Segment[]> {
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,
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>Clear Service Worker</title>
</head>
<body>
<h1>清除 Service Worker</h1>
<button id="clearBtn">点击清除所有 Service Workers 和缓存</button>
<div id="status"></div>
<script>
document.getElementById('clearBtn').addEventListener('click', async () => {
const status = document.getElementById('status');
try {
// Unregister all service workers
if ('serviceWorker' in navigator) {
const registrations = await navigator.serviceWorker.getRegistrations();
for (const registration of registrations) {
await registration.unregister();
}
status.innerHTML += '<p>✅ Service Workers 已注销</p>';
}
// Clear all caches
if ('caches' in window) {
const names = await caches.keys();
await Promise.all(names.map(name => caches.delete(name)));
status.innerHTML += '<p>✅ 所有缓存已清除</p>';
}
status.innerHTML += '<p><strong>完成!请刷新页面。</strong></p>';
} catch (error) {
status.innerHTML += `<p>❌ 错误: ${error.message}</p>`;
}
});
</script>
</body>
</html>
+6 -1
View File
@@ -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(