修复压缩节目单无法加载
This commit is contained in:
88
src/app/api/live/epg/download/route.ts
Normal file
88
src/app/api/live/epg/download/route.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const epgUrl = searchParams.get('url');
|
||||
|
||||
if (!epgUrl) {
|
||||
return NextResponse.json({ error: '缺少EPG URL参数' }, { status: 400 });
|
||||
}
|
||||
|
||||
console.log('[EPG Download] Fetching:', epgUrl);
|
||||
|
||||
// 获取EPG文件
|
||||
const response = await fetch(epgUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return NextResponse.json({ error: 'EPG文件下载失败' }, { status: 500 });
|
||||
}
|
||||
|
||||
// 检查是否是gzip压缩
|
||||
const isGzip = epgUrl.endsWith('.gz') || response.headers.get('content-encoding') === 'gzip';
|
||||
|
||||
if (isGzip) {
|
||||
console.log('[EPG Download] Decompressing gzip...');
|
||||
|
||||
// 读取所有数据
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
return NextResponse.json({ error: '无法读取响应' }, { status: 500 });
|
||||
}
|
||||
|
||||
const chunks: Uint8Array[] = [];
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(value);
|
||||
}
|
||||
|
||||
// 合并所有chunks
|
||||
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
||||
const allChunks = new Uint8Array(totalLength);
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
allChunks.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
|
||||
console.log('[EPG Download] Compressed size:', totalLength, 'bytes');
|
||||
|
||||
// 解压
|
||||
const zlib = await import('zlib');
|
||||
const decompressed = zlib.gunzipSync(Buffer.from(allChunks));
|
||||
const decompressedText = decompressed.toString('utf-8');
|
||||
|
||||
console.log('[EPG Download] Decompressed size:', decompressedText.length, 'bytes');
|
||||
|
||||
// 返回解压后的XML
|
||||
return new NextResponse(decompressedText, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
'Content-Disposition': 'attachment; filename="epg.xml"',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 非压缩文件,直接返回
|
||||
const text = await response.text();
|
||||
return new NextResponse(text, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
'Content-Disposition': 'attachment; filename="epg.xml"',
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[EPG Download] Error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '下载EPG文件失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -360,9 +360,12 @@ function LivePageClient() {
|
||||
|
||||
// 默认选中第一个频道
|
||||
if (channels.length > 0) {
|
||||
let selectedChannel: LiveChannel | null = null;
|
||||
|
||||
if (needLoadChannel) {
|
||||
const foundChannel = channels.find((c: LiveChannel) => c.id === needLoadChannel);
|
||||
if (foundChannel) {
|
||||
selectedChannel = foundChannel;
|
||||
setCurrentChannel(foundChannel);
|
||||
setVideoUrl(foundChannel.url);
|
||||
// 延迟滚动到选中的频道
|
||||
@@ -370,13 +373,20 @@ function LivePageClient() {
|
||||
scrollToChannel(foundChannel);
|
||||
}, 200);
|
||||
} else {
|
||||
selectedChannel = channels[0];
|
||||
setCurrentChannel(channels[0]);
|
||||
setVideoUrl(channels[0].url);
|
||||
}
|
||||
} else {
|
||||
selectedChannel = channels[0];
|
||||
setCurrentChannel(channels[0]);
|
||||
setVideoUrl(channels[0].url);
|
||||
}
|
||||
|
||||
// 获取初始频道的节目单
|
||||
if (selectedChannel) {
|
||||
await fetchEpgData(selectedChannel, source);
|
||||
}
|
||||
}
|
||||
|
||||
// 按分组组织频道
|
||||
@@ -466,6 +476,35 @@ function LivePageClient() {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取节目单信息的辅助函数
|
||||
const fetchEpgData = async (channel: LiveChannel, source: LiveSource) => {
|
||||
if (channel.tvgId && source) {
|
||||
try {
|
||||
setIsEpgLoading(true); // 开始加载 EPG 数据
|
||||
const response = await fetch(`/api/live/epg?source=${source.key}&tvgId=${channel.tvgId}`);
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
// 清洗EPG数据,去除重叠的节目
|
||||
const cleanedData = {
|
||||
...result.data,
|
||||
programs: cleanEpgData(result.data.programs)
|
||||
};
|
||||
setEpgData(cleanedData);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取节目单信息失败:', error);
|
||||
} finally {
|
||||
setIsEpgLoading(false); // 无论成功失败都结束加载状态
|
||||
}
|
||||
} else {
|
||||
// 如果没有 tvgId 或 source,清空 EPG 数据
|
||||
setEpgData(null);
|
||||
setIsEpgLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 切换频道
|
||||
const handleChannelChange = async (channel: LiveChannel) => {
|
||||
// 如果正在切换直播源,则禁用频道切换
|
||||
@@ -486,30 +525,8 @@ function LivePageClient() {
|
||||
}, 100);
|
||||
|
||||
// 获取节目单信息
|
||||
if (channel.tvgId && currentSource) {
|
||||
try {
|
||||
setIsEpgLoading(true); // 开始加载 EPG 数据
|
||||
const response = await fetch(`/api/live/epg?source=${currentSource.key}&tvgId=${channel.tvgId}`);
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
// 清洗EPG数据,去除重叠的节目
|
||||
const cleanedData = {
|
||||
...result.data,
|
||||
programs: cleanEpgData(result.data.programs)
|
||||
};
|
||||
setEpgData(cleanedData);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取节目单信息失败:', error);
|
||||
} finally {
|
||||
setIsEpgLoading(false); // 无论成功失败都结束加载状态
|
||||
}
|
||||
} else {
|
||||
// 如果没有 tvgId 或 currentSource,清空 EPG 数据
|
||||
setEpgData(null);
|
||||
setIsEpgLoading(false);
|
||||
if (currentSource) {
|
||||
await fetchEpgData(channel, currentSource);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user