mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-28 01:13:15 +08:00
feat: add live logo proxy
This commit is contained in:
66
src/app/api/proxy/logo/route.ts
Normal file
66
src/app/api/proxy/logo/route.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { getConfig } from '@/lib/config';
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
const imageUrl = searchParams.get('url');
|
||||||
|
const source = searchParams.get('moontv-source');
|
||||||
|
|
||||||
|
if (!imageUrl) {
|
||||||
|
return NextResponse.json({ error: 'Missing image URL' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await getConfig();
|
||||||
|
const liveSource = config.LiveConfig?.find((s: any) => s.key === source);
|
||||||
|
const ua = liveSource?.ua || 'AptvPlayer/1.4.10';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decodedUrl = decodeURIComponent(imageUrl);
|
||||||
|
const imageResponse = await fetch(decodedUrl, {
|
||||||
|
cache: 'no-cache',
|
||||||
|
redirect: 'follow',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
'User-Agent': ua,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!imageResponse.ok) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: imageResponse.statusText },
|
||||||
|
{ status: imageResponse.status }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = imageResponse.headers.get('content-type');
|
||||||
|
|
||||||
|
if (!imageResponse.body) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Image response has no body' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建响应头
|
||||||
|
const headers = new Headers();
|
||||||
|
if (contentType) {
|
||||||
|
headers.set('Content-Type', contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置缓存头
|
||||||
|
headers.set('Cache-Control', 'public, max-age=86400, s-maxage=86400'); // 缓存一天
|
||||||
|
|
||||||
|
// 直接返回图片流
|
||||||
|
return new Response(imageResponse.body, {
|
||||||
|
status: 200,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Error fetching image' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import { Radio, Tv } from 'lucide-react';
|
|||||||
import { Suspense, useEffect, useRef, useState } from 'react';
|
import { Suspense, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { parseCustomTimeFormat } from '@/lib/time';
|
import { parseCustomTimeFormat } from '@/lib/time';
|
||||||
import { processImageUrl } from '@/lib/utils';
|
|
||||||
|
|
||||||
import EpgScrollableRow from '@/components/EpgScrollableRow';
|
import EpgScrollableRow from '@/components/EpgScrollableRow';
|
||||||
import PageLayout from '@/components/PageLayout';
|
import PageLayout from '@/components/PageLayout';
|
||||||
@@ -1132,9 +1131,10 @@ function LivePageClient() {
|
|||||||
<div className='w-10 h-10 bg-gray-300 dark:bg-gray-700 rounded-lg flex items-center justify-center flex-shrink-0 overflow-hidden'>
|
<div className='w-10 h-10 bg-gray-300 dark:bg-gray-700 rounded-lg flex items-center justify-center flex-shrink-0 overflow-hidden'>
|
||||||
{channel.logo ? (
|
{channel.logo ? (
|
||||||
<img
|
<img
|
||||||
src={processImageUrl(channel.logo)}
|
src={`/api/proxy/logo?url=${encodeURIComponent(channel.logo)}&source=${currentSource?.key || ''}`}
|
||||||
alt={channel.name}
|
alt={channel.name}
|
||||||
className='w-full h-full rounded object-contain'
|
className='w-full h-full rounded object-contain'
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Tv className='w-5 h-5 text-gray-500' />
|
<Tv className='w-5 h-5 text-gray-500' />
|
||||||
@@ -1239,9 +1239,10 @@ function LivePageClient() {
|
|||||||
<div className='w-20 h-20 bg-gray-300 dark:bg-gray-700 rounded-lg flex items-center justify-center flex-shrink-0 overflow-hidden'>
|
<div className='w-20 h-20 bg-gray-300 dark:bg-gray-700 rounded-lg flex items-center justify-center flex-shrink-0 overflow-hidden'>
|
||||||
{currentChannel.logo ? (
|
{currentChannel.logo ? (
|
||||||
<img
|
<img
|
||||||
src={processImageUrl(currentChannel.logo)}
|
src={`/api/proxy/logo?url=${encodeURIComponent(currentChannel.logo)}&source=${currentSource?.key || ''}`}
|
||||||
alt={currentChannel.name}
|
alt={currentChannel.name}
|
||||||
className='w-full h-full rounded object-contain'
|
className='w-full h-full rounded object-contain'
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Tv className='w-10 h-10 text-gray-500' />
|
<Tv className='w-10 h-10 text-gray-500' />
|
||||||
|
|||||||
Reference in New Issue
Block a user