162 lines
4.5 KiB
TypeScript
162 lines
4.5 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
|
|
import { getAuthInfoFromCookie } from '@/lib/auth';
|
|
import { getAvailableApiSites, getConfig } from '@/lib/config';
|
|
import { getCachedLiveChannels } from '@/lib/live';
|
|
|
|
export const runtime = 'nodejs';
|
|
|
|
/**
|
|
* TVBOX订阅API
|
|
* 根据视频源和直播源生成TVBOX订阅
|
|
*/
|
|
export async function GET(request: NextRequest) {
|
|
// 检查是否开启订阅功能
|
|
const enableSubscribe = process.env.ENABLE_TVBOX_SUBSCRIBE === 'true';
|
|
if (!enableSubscribe) {
|
|
return NextResponse.json(
|
|
{ error: '订阅功能未开启' },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
|
|
// 验证token
|
|
const searchParams = request.nextUrl.searchParams;
|
|
const token = searchParams.get('token');
|
|
const subscribeToken = process.env.TVBOX_SUBSCRIBE_TOKEN;
|
|
const adFilter = searchParams.get('adFilter') === 'true'; // 获取去广告参数
|
|
|
|
if (!subscribeToken || token !== subscribeToken) {
|
|
return NextResponse.json(
|
|
{ error: '无效的订阅token' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
try {
|
|
// 获取用户信息
|
|
const authInfo = getAuthInfoFromCookie(request);
|
|
const username = authInfo?.username;
|
|
|
|
// 获取配置
|
|
const config = await getConfig();
|
|
|
|
// 获取视频源
|
|
const apiSites = await getAvailableApiSites(username);
|
|
|
|
// 获取直播源
|
|
const liveConfig = config.LiveConfig?.filter(live => !live.disabled) || [];
|
|
|
|
// 获取当前请求的 origin,用于构建代理链接
|
|
// 优先级:SITE_BASE 环境变量 > origin 参数 > 从请求头构建
|
|
let baseUrl = process.env.SITE_BASE || searchParams.get('origin');
|
|
|
|
if (!baseUrl) {
|
|
// 从请求头中获取 Host 和协议
|
|
const host = request.headers.get('host') || request.headers.get('x-forwarded-host');
|
|
const proto = request.headers.get('x-forwarded-proto') ||
|
|
(host?.includes('localhost') || host?.includes('127.0.0.1') ? 'http' : 'https');
|
|
baseUrl = `${proto}://${host}`;
|
|
}
|
|
|
|
console.log('TVBOX 订阅 baseUrl:', baseUrl, 'adFilter:', adFilter);
|
|
|
|
// 检查是否配置了 OpenList
|
|
const hasOpenList = !!(
|
|
config.OpenListConfig?.Enabled &&
|
|
config.OpenListConfig?.URL &&
|
|
config.OpenListConfig?.Username &&
|
|
config.OpenListConfig?.Password
|
|
);
|
|
|
|
// 构建 OpenList 站点配置
|
|
const openlistSites = hasOpenList ? [{
|
|
key: 'openlist',
|
|
name: '私人影库',
|
|
type: 1,
|
|
api: `${baseUrl}/api/openlist/cms-proxy/${encodeURIComponent(subscribeToken)}`,
|
|
searchable: 1,
|
|
quickSearch: 1,
|
|
filterable: 1,
|
|
ext: '',
|
|
}] : [];
|
|
|
|
// 构建TVBOX订阅数据
|
|
const tvboxSubscription = {
|
|
// 站点配置
|
|
spider: '',
|
|
wallpaper: '',
|
|
|
|
// 视频源站点 - 根据 adFilter 参数决定是否使用代理
|
|
// OpenList 源放在最前面
|
|
sites: [
|
|
...openlistSites,
|
|
...apiSites.map(site => ({
|
|
key: site.key,
|
|
name: site.name,
|
|
type: 1,
|
|
// 如果开启去广告,使用 CMS 代理;否则使用原始 API
|
|
api: adFilter
|
|
? `${baseUrl}/api/cms-proxy?api=${encodeURIComponent(site.api)}`
|
|
: site.api,
|
|
searchable: 1,
|
|
quickSearch: 1,
|
|
filterable: 1,
|
|
ext: site.detail || '',
|
|
}))
|
|
],
|
|
|
|
// 直播源
|
|
lives: await Promise.all(
|
|
liveConfig.map(async (live) => {
|
|
try {
|
|
const liveChannels = await getCachedLiveChannels(live.key);
|
|
return {
|
|
name: live.name,
|
|
type: 0,
|
|
url: live.url,
|
|
epg: live.epg || (liveChannels?.epgUrl || ''),
|
|
logo: '',
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
name: live.name,
|
|
type: 0,
|
|
url: live.url,
|
|
epg: live.epg || '',
|
|
logo: '',
|
|
};
|
|
}
|
|
})
|
|
),
|
|
|
|
// 解析器
|
|
parses: [],
|
|
|
|
// 规则
|
|
rules: [],
|
|
|
|
// 广告配置
|
|
ads: [],
|
|
};
|
|
|
|
return NextResponse.json(tvboxSubscription, {
|
|
headers: {
|
|
'Content-Type': 'application/json; charset=utf-8',
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('生成TVBOX订阅失败:', error);
|
|
return NextResponse.json(
|
|
{
|
|
error: '生成订阅失败',
|
|
details: (error as Error).message,
|
|
},
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|