feat: 添加 TVBox 配置接口,支持视频源导入及解析功能
This commit is contained in:
@@ -43,6 +43,7 @@
|
|||||||
- **☁️ 多平台支持**:Vercel、Cloudflare Pages、传统服务器全兼容
|
- **☁️ 多平台支持**:Vercel、Cloudflare Pages、传统服务器全兼容
|
||||||
- **🔧 灵活配置**:支持自定义资源站、代理设置、主题配置
|
- **🔧 灵活配置**:支持自定义资源站、代理设置、主题配置
|
||||||
- **📱 PWA 支持**:可安装为桌面/手机应用,离线缓存
|
- **📱 PWA 支持**:可安装为桌面/手机应用,离线缓存
|
||||||
|
- **📺 TVBox 兼容**:支持 TVBox 配置接口,可导入到各种电视盒子应用
|
||||||
|
|
||||||
### 🎨 用户体验
|
### 🎨 用户体验
|
||||||
|
|
||||||
@@ -63,6 +64,32 @@
|
|||||||
| 代码质量 | ESLint · Prettier · Jest · Husky |
|
| 代码质量 | ESLint · Prettier · Jest · Husky |
|
||||||
| 部署 | Docker · Vercel · CloudFlare pages |
|
| 部署 | Docker · Vercel · CloudFlare pages |
|
||||||
|
|
||||||
|
## 📺 TVBox 兼容功能
|
||||||
|
|
||||||
|
KatelyaTV 新增了 TVBox 配置接口,可以将您的视频源导入到各种电视盒子应用中使用:
|
||||||
|
|
||||||
|
### ✨ 功能特点
|
||||||
|
|
||||||
|
- **🔄 自动同步**:自动同步 KatelyaTV 中配置的所有视频源
|
||||||
|
- **📋 标准格式**:支持 TVBox 标准 JSON 配置格式
|
||||||
|
- **🎬 内置解析**:集成多个视频解析接口,支持主流视频平台
|
||||||
|
- **🌐 跨域支持**:自动处理 CORS 跨域问题
|
||||||
|
- **📱 多格式**:支持 JSON 和 Base64 两种配置格式
|
||||||
|
|
||||||
|
### 🚀 快速使用
|
||||||
|
|
||||||
|
1. **访问配置页面**:在 KatelyaTV 中点击侧边栏的"TVBox 配置"
|
||||||
|
2. **复制配置链接**:选择适合的格式并复制链接
|
||||||
|
3. **导入到 TVBox**:在 TVBox 应用中导入配置链接
|
||||||
|
|
||||||
|
### 🔗 API 端点
|
||||||
|
|
||||||
|
- **JSON 配置**:`https://your-domain.com/api/tvbox`
|
||||||
|
- **Base64 配置**:`https://your-domain.com/api/tvbox?format=txt`
|
||||||
|
- **视频解析**:`https://your-domain.com/api/parse?url={视频地址}`
|
||||||
|
|
||||||
|
> 📖 详细使用说明请查看:[TVBox 配置指南](docs/TVBOX.md)
|
||||||
|
|
||||||
## 🚀 部署教程
|
## 🚀 部署教程
|
||||||
|
|
||||||
> **💡 推荐方案**:
|
> **💡 推荐方案**:
|
||||||
|
|||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
# TVBox 配置接口使用指南
|
||||||
|
|
||||||
|
## 📺 功能介绍
|
||||||
|
|
||||||
|
KatelyaTV 现在支持 TVBox 配置接口,可以将您的视频源直接导入到 TVBox 应用中使用。这个功能会自动同步 KatelyaTV 中配置的所有视频源,并提供标准的 TVBox JSON 格式配置。
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 1. 访问配置页面
|
||||||
|
|
||||||
|
在 KatelyaTV 网站中,点击左侧导航栏的"TVBox 配置"菜单,或直接访问:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://your-domain.com/tvbox
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 复制配置链接
|
||||||
|
|
||||||
|
选择以下任一配置格式:
|
||||||
|
|
||||||
|
**JSON 格式(推荐):**
|
||||||
|
|
||||||
|
```
|
||||||
|
https://your-domain.com/api/tvbox
|
||||||
|
```
|
||||||
|
|
||||||
|
**Base64 格式:**
|
||||||
|
|
||||||
|
```
|
||||||
|
https://your-domain.com/api/tvbox?format=txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 导入到 TVBox
|
||||||
|
|
||||||
|
1. 打开 TVBox 应用
|
||||||
|
2. 进入设置 → 配置地址
|
||||||
|
3. 粘贴复制的配置链接
|
||||||
|
4. 点击确认导入
|
||||||
|
|
||||||
|
## 🔧 配置说明
|
||||||
|
|
||||||
|
### 支持的功能
|
||||||
|
|
||||||
|
- ✅ 自动同步 KatelyaTV 的所有视频源
|
||||||
|
- ✅ 支持搜索功能
|
||||||
|
- ✅ 支持快速搜索
|
||||||
|
- ✅ 支持分类筛选
|
||||||
|
- ✅ 内置视频解析接口
|
||||||
|
- ✅ 广告过滤规则
|
||||||
|
- ✅ CORS 跨域支持
|
||||||
|
|
||||||
|
### 内置解析接口
|
||||||
|
|
||||||
|
KatelyaTV 提供内置的视频解析服务:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://your-domain.com/api/parse?url={视频地址}
|
||||||
|
```
|
||||||
|
|
||||||
|
支持的平台:
|
||||||
|
|
||||||
|
- 腾讯视频 (qq.com)
|
||||||
|
- 爱奇艺 (iqiyi.com)
|
||||||
|
- 优酷 (youku.com)
|
||||||
|
- 芒果 TV (mgtv.com)
|
||||||
|
- 哔哩哔哩 (bilibili.com)
|
||||||
|
- 搜狐视频 (sohu.com)
|
||||||
|
- 乐视 (letv.com)
|
||||||
|
- 土豆 (tudou.com)
|
||||||
|
- PPTV (pptv.com)
|
||||||
|
- 1905 电影网 (1905.com)
|
||||||
|
|
||||||
|
### 解析接口参数
|
||||||
|
|
||||||
|
- `url`: 要解析的视频地址(必填)
|
||||||
|
- `parser`: 指定解析器名称(可选)
|
||||||
|
- `format`: 返回格式,支持 `json`、`redirect`、`iframe`(可选,默认 json)
|
||||||
|
|
||||||
|
## 📝 API 端点说明
|
||||||
|
|
||||||
|
### TVBox 配置接口
|
||||||
|
|
||||||
|
**GET** `/api/tvbox`
|
||||||
|
|
||||||
|
**参数:**
|
||||||
|
|
||||||
|
- `format`: 返回格式
|
||||||
|
- `json`(默认):返回 JSON 格式配置
|
||||||
|
- `txt`:返回 Base64 编码的配置
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sites": [...], // 影视源列表
|
||||||
|
"parses": [...], // 解析源列表
|
||||||
|
"flags": [...], // 播放标识
|
||||||
|
"ads": [...], // 广告过滤规则
|
||||||
|
"wallpaper": "...", // 壁纸地址
|
||||||
|
"lives": [...] // 直播源(可选)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 视频解析接口
|
||||||
|
|
||||||
|
**GET** `/api/parse`
|
||||||
|
|
||||||
|
**参数:**
|
||||||
|
|
||||||
|
- `url`: 视频地址
|
||||||
|
- `parser`: 解析器名称(可选)
|
||||||
|
- `format`: 返回格式(可选)
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"original_url": "...",
|
||||||
|
"platform": "qq",
|
||||||
|
"parse_url": "...",
|
||||||
|
"parser_name": "...",
|
||||||
|
"available_parsers": [...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 配置更新
|
||||||
|
|
||||||
|
当您在 KatelyaTV 中添加、修改或删除视频源时:
|
||||||
|
|
||||||
|
1. TVBox 配置会自动同步最新的源站信息
|
||||||
|
2. 在 TVBox 中刷新配置即可获取最新源站
|
||||||
|
3. 无需手动更新配置链接
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
1. **网络要求**:确保 TVBox 设备能够访问您的 KatelyaTV 服务器
|
||||||
|
2. **HTTPS 支持**:建议使用 HTTPS 协议确保安全性
|
||||||
|
3. **缓存设置**:配置会缓存 1 小时,如需立即更新请刷新 TVBox 配置
|
||||||
|
4. **兼容性**:支持 TVBox 及其衍生应用
|
||||||
|
5. **源站限制**:解析效果取决于原始视频源的可用性
|
||||||
|
|
||||||
|
## 🛠️ 故障排除
|
||||||
|
|
||||||
|
### 配置导入失败
|
||||||
|
|
||||||
|
- 检查网络连接
|
||||||
|
- 确认配置链接格式正确
|
||||||
|
- 尝试使用不同的 format 参数
|
||||||
|
|
||||||
|
### 视频无法播放
|
||||||
|
|
||||||
|
- 检查原始视频源是否可用
|
||||||
|
- 尝试使用不同的解析器
|
||||||
|
- 确认视频平台是否被支持
|
||||||
|
|
||||||
|
### 源站不显示
|
||||||
|
|
||||||
|
- 检查 KatelyaTV 中是否正确配置了视频源
|
||||||
|
- 确认视频源格式符合要求
|
||||||
|
- 刷新 TVBox 配置
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
如果您在使用过程中遇到问题,请:
|
||||||
|
|
||||||
|
1. 检查上述故障排除方案
|
||||||
|
2. 查看 KatelyaTV 和 TVBox 的日志信息
|
||||||
|
3. 向项目仓库提交 Issue
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_此功能基于 TVBox 标准 JSON 配置格式开发,兼容大部分 TVBox 及其衍生应用。_
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
// 常用的视频解析接口列表
|
||||||
|
const PARSE_APIS = [
|
||||||
|
{
|
||||||
|
name: '无名小站',
|
||||||
|
url: 'https://jx.aidouer.net/?url=',
|
||||||
|
support: ['qq', 'iqiyi', 'youku', 'mgtv', 'bilibili']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '虾米解析',
|
||||||
|
url: 'https://jx.xmflv.com/?url=',
|
||||||
|
support: ['qq', 'iqiyi', 'youku', 'mgtv', 'bilibili', 'sohu']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '爱豆解析',
|
||||||
|
url: 'https://jx.aidouer.net/?url=',
|
||||||
|
support: ['qq', 'iqiyi', 'youku', 'mgtv', 'bilibili']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '8090解析',
|
||||||
|
url: 'https://www.8090g.cn/?url=',
|
||||||
|
support: ['qq', 'iqiyi', 'youku', 'mgtv', 'bilibili']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'OK解析',
|
||||||
|
url: 'https://okjx.cc/?url=',
|
||||||
|
support: ['qq', 'iqiyi', 'youku', 'mgtv', 'bilibili']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 检测视频URL的平台类型
|
||||||
|
function detectPlatform(url: string): string {
|
||||||
|
if (url.includes('qq.com') || url.includes('v.qq.com')) return 'qq';
|
||||||
|
if (url.includes('iqiyi.com') || url.includes('qiyi.com')) return 'iqiyi';
|
||||||
|
if (url.includes('youku.com')) return 'youku';
|
||||||
|
if (url.includes('mgtv.com')) return 'mgtv';
|
||||||
|
if (url.includes('bilibili.com')) return 'bilibili';
|
||||||
|
if (url.includes('sohu.com')) return 'sohu';
|
||||||
|
if (url.includes('letv.com') || url.includes('le.com')) return 'letv';
|
||||||
|
if (url.includes('tudou.com')) return 'tudou';
|
||||||
|
if (url.includes('pptv.com')) return 'pptv';
|
||||||
|
if (url.includes('1905.com')) return '1905';
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取适用的解析接口
|
||||||
|
function getCompatibleParsers(platform: string) {
|
||||||
|
return PARSE_APIS.filter(api =>
|
||||||
|
api.support.includes(platform) || platform === 'unknown'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
const url = searchParams.get('url');
|
||||||
|
const parser = searchParams.get('parser'); // 指定解析器
|
||||||
|
const format = searchParams.get('format') || 'json'; // 返回格式
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '缺少url参数' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测平台类型
|
||||||
|
const platform = detectPlatform(url);
|
||||||
|
const compatibleParsers = getCompatibleParsers(platform);
|
||||||
|
|
||||||
|
if (compatibleParsers.length === 0) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: '暂不支持该平台的视频解析',
|
||||||
|
platform,
|
||||||
|
url
|
||||||
|
},
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了解析器,优先使用
|
||||||
|
let selectedParser = compatibleParsers[0];
|
||||||
|
if (parser) {
|
||||||
|
const customParser = PARSE_APIS.find(api =>
|
||||||
|
api.name.toLowerCase().includes(parser.toLowerCase())
|
||||||
|
);
|
||||||
|
if (customParser && compatibleParsers.includes(customParser)) {
|
||||||
|
selectedParser = customParser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseUrl = selectedParser.url + encodeURIComponent(url);
|
||||||
|
|
||||||
|
// 根据format返回不同格式
|
||||||
|
if (format === 'redirect') {
|
||||||
|
// 直接重定向到解析页面
|
||||||
|
return NextResponse.redirect(parseUrl);
|
||||||
|
} else if (format === 'iframe') {
|
||||||
|
// 返回可嵌入的HTML页面
|
||||||
|
const html = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>视频播放</title>
|
||||||
|
<style>
|
||||||
|
body { margin: 0; padding: 0; background: #000; }
|
||||||
|
iframe { width: 100%; height: 100vh; border: none; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe src="${parseUrl}" allowfullscreen></iframe>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
return new NextResponse(html, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/html; charset=utf-8',
|
||||||
|
'Access-Control-Allow-Origin': '*'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 返回JSON格式的解析信息
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
original_url: url,
|
||||||
|
platform,
|
||||||
|
parse_url: parseUrl,
|
||||||
|
parser_name: selectedParser.name,
|
||||||
|
available_parsers: compatibleParsers.map(p => p.name)
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
'Cache-Control': 'public, max-age=300' // 5分钟缓存
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: '视频解析失败',
|
||||||
|
details: error instanceof Error ? error.message : String(error)
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持CORS预检请求
|
||||||
|
export async function OPTIONS() {
|
||||||
|
return new NextResponse(null, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
// 定义配置文件结构
|
||||||
|
interface ApiSite {
|
||||||
|
api: string;
|
||||||
|
name: string;
|
||||||
|
detail?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConfigFileStruct {
|
||||||
|
cache_time?: number;
|
||||||
|
api_site: {
|
||||||
|
[key: string]: ApiSite;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TVBox源格式接口
|
||||||
|
interface TVBoxSource {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
type: number; // 0=影视源, 1=直播源, 3=解析源
|
||||||
|
api: string;
|
||||||
|
searchable?: number; // 0=不可搜索, 1=可搜索
|
||||||
|
quickSearch?: number; // 0=不支持快速搜索, 1=支持快速搜索
|
||||||
|
filterable?: number; // 0=不支持分类筛选, 1=支持分类筛选
|
||||||
|
ext?: string; // 扩展参数
|
||||||
|
jar?: string; // jar包地址
|
||||||
|
playUrl?: string; // 播放解析地址
|
||||||
|
categories?: string[]; // 分类
|
||||||
|
timeout?: number; // 超时时间(秒)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TVBoxConfig {
|
||||||
|
spider?: string; // 爬虫jar包地址
|
||||||
|
wallpaper?: string; // 壁纸地址
|
||||||
|
lives?: Array<{
|
||||||
|
name: string;
|
||||||
|
type: number;
|
||||||
|
url: string;
|
||||||
|
epg?: string;
|
||||||
|
logo?: string;
|
||||||
|
}>; // 直播源
|
||||||
|
sites: TVBoxSource[]; // 影视源
|
||||||
|
parses?: Array<{
|
||||||
|
name: string;
|
||||||
|
type: number;
|
||||||
|
url: string;
|
||||||
|
ext?: Record<string, unknown>;
|
||||||
|
header?: Record<string, string>;
|
||||||
|
}>; // 解析源
|
||||||
|
flags?: string[]; // 播放标识
|
||||||
|
ijk?: Record<string, unknown>; // IJK播放器配置
|
||||||
|
ads?: string[]; // 广告过滤规则
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取配置文件
|
||||||
|
function readConfigFile(): ConfigFileStruct {
|
||||||
|
const configPath = path.join(process.cwd(), 'config.json');
|
||||||
|
const raw = fs.readFileSync(configPath, 'utf-8');
|
||||||
|
return JSON.parse(raw) as ConfigFileStruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
const format = searchParams.get('format') || 'json'; // 支持json和txt格式
|
||||||
|
const host = request.headers.get('host') || 'localhost:3000';
|
||||||
|
const protocol = request.headers.get('x-forwarded-proto') || 'http';
|
||||||
|
const baseUrl = `${protocol}://${host}`;
|
||||||
|
|
||||||
|
// 读取当前配置
|
||||||
|
const config = readConfigFile();
|
||||||
|
if (!config?.api_site) {
|
||||||
|
return NextResponse.json({ error: '配置文件读取失败' }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为TVBox格式
|
||||||
|
const tvboxConfig: TVBoxConfig = {
|
||||||
|
// 基础配置
|
||||||
|
spider: '', // 可以根据需要添加爬虫jar包
|
||||||
|
wallpaper: `${baseUrl}/screenshot1.png`, // 使用项目截图作为壁纸
|
||||||
|
|
||||||
|
// 影视源配置
|
||||||
|
sites: Object.entries(config.api_site).map(([key, source]) => ({
|
||||||
|
key: key,
|
||||||
|
name: source.name,
|
||||||
|
type: 0, // 影视源
|
||||||
|
api: source.api,
|
||||||
|
searchable: 1, // 可搜索
|
||||||
|
quickSearch: 1, // 支持快速搜索
|
||||||
|
filterable: 1, // 支持分类筛选
|
||||||
|
ext: source.detail || '', // 详情页地址作为扩展参数
|
||||||
|
timeout: 30, // 30秒超时
|
||||||
|
categories: [
|
||||||
|
"电影", "电视剧", "综艺", "动漫", "纪录片", "短剧"
|
||||||
|
]
|
||||||
|
})),
|
||||||
|
|
||||||
|
// 解析源配置(添加一些常用的解析源)
|
||||||
|
parses: [
|
||||||
|
{
|
||||||
|
name: "Json并发",
|
||||||
|
type: 2,
|
||||||
|
url: "Parallel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Json轮询",
|
||||||
|
type: 2,
|
||||||
|
url: "Sequence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "KatelyaTV内置解析",
|
||||||
|
type: 1,
|
||||||
|
url: `${baseUrl}/api/parse?url=`,
|
||||||
|
ext: {
|
||||||
|
flag: ["qiyi", "qq", "letv", "sohu", "youku", "mgtv", "bilibili", "wasu", "xigua", "1905"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// 播放标识
|
||||||
|
flags: [
|
||||||
|
"youku", "qq", "iqiyi", "qiyi", "letv", "sohu", "tudou", "pptv",
|
||||||
|
"mgtv", "wasu", "bilibili", "le", "duoduozy", "renrenmi", "xigua",
|
||||||
|
"优酷", "腾讯", "爱奇艺", "奇艺", "乐视", "搜狐", "土豆", "PPTV",
|
||||||
|
"芒果", "华数", "哔哩", "1905"
|
||||||
|
],
|
||||||
|
|
||||||
|
// 直播源(可选)
|
||||||
|
lives: [
|
||||||
|
{
|
||||||
|
name: "KatelyaTV直播",
|
||||||
|
type: 0,
|
||||||
|
url: `${baseUrl}/api/live/channels`,
|
||||||
|
epg: "",
|
||||||
|
logo: ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// 广告过滤规则
|
||||||
|
ads: [
|
||||||
|
"mimg.0c1q0l.cn",
|
||||||
|
"www.googletagmanager.com",
|
||||||
|
"www.google-analytics.com",
|
||||||
|
"mc.usihnbcq.cn",
|
||||||
|
"mg.g1mm3d.cn",
|
||||||
|
"mscs.svaeuzh.cn",
|
||||||
|
"cnzz.hhurm.com",
|
||||||
|
"tp.vinuxhome.com",
|
||||||
|
"cnzz.mmstat.com",
|
||||||
|
"www.baihuillq.com",
|
||||||
|
"s23.cnzz.com",
|
||||||
|
"z3.cnzz.com",
|
||||||
|
"c.cnzz.com",
|
||||||
|
"stj.v1vo.top",
|
||||||
|
"z12.cnzz.com",
|
||||||
|
"img.mosflower.cn",
|
||||||
|
"tips.gamevvip.com",
|
||||||
|
"ehwe.yhdtns.com",
|
||||||
|
"xdn.cqqc3.com",
|
||||||
|
"www.jixunkyy.cn",
|
||||||
|
"sp.chemacid.cn",
|
||||||
|
"hm.baidu.com",
|
||||||
|
"s9.cnzz.com",
|
||||||
|
"z6.cnzz.com",
|
||||||
|
"um.cavuc.com",
|
||||||
|
"mav.mavuz.com",
|
||||||
|
"wofwk.aoidf3.com",
|
||||||
|
"z5.cnzz.com",
|
||||||
|
"xc.hubeijieshikj.cn",
|
||||||
|
"tj.tianwenhu.com",
|
||||||
|
"xg.gars57.cn",
|
||||||
|
"k.jinxiuzhilv.com",
|
||||||
|
"cdn.bootcss.com",
|
||||||
|
"ppl.xunzhuo123.com",
|
||||||
|
"xomk.jiangjunmh.top",
|
||||||
|
"img.xunzhuo123.com",
|
||||||
|
"z1.cnzz.com",
|
||||||
|
"s13.cnzz.com",
|
||||||
|
"xg.huataisangao.cn",
|
||||||
|
"z7.cnzz.com",
|
||||||
|
"xg.huataisangao.cn",
|
||||||
|
"z2.cnzz.com",
|
||||||
|
"s96.cnzz.com",
|
||||||
|
"q11.cnzz.com",
|
||||||
|
"thy.dacedsfa.cn",
|
||||||
|
"xg.whsbpw.cn",
|
||||||
|
"s19.cnzz.com",
|
||||||
|
"z8.cnzz.com",
|
||||||
|
"s4.cnzz.com",
|
||||||
|
"f5w.as12df.top",
|
||||||
|
"ae01.alicdn.com",
|
||||||
|
"www.92424.cn",
|
||||||
|
"k.wudejia.com",
|
||||||
|
"vivovip.mmszxc.top",
|
||||||
|
"qiu.xixiqiu.com",
|
||||||
|
"cdnjs.hnfenxun.com",
|
||||||
|
"cms.qdwght.com"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据format参数返回不同格式
|
||||||
|
if (format === 'txt') {
|
||||||
|
// 返回base64编码的配置(TVBox常用格式)
|
||||||
|
const configStr = JSON.stringify(tvboxConfig, null, 2);
|
||||||
|
const base64Config = Buffer.from(configStr).toString('base64');
|
||||||
|
|
||||||
|
return new NextResponse(base64Config, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain; charset=utf-8',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
'Cache-Control': 'public, max-age=3600'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 返回JSON格式
|
||||||
|
return NextResponse.json(tvboxConfig, {
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
'Cache-Control': 'public, max-age=3600'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'TVBox配置生成失败', details: error instanceof Error ? error.message : String(error) },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持CORS预检请求
|
||||||
|
export async function OPTIONS() {
|
||||||
|
return new NextResponse(null, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import PageLayout from '@/components/PageLayout';
|
||||||
|
|
||||||
|
export default function TVBoxPage() {
|
||||||
|
const [baseUrl, setBaseUrl] = useState('');
|
||||||
|
const [copySuccess, setCopySuccess] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 获取当前域名
|
||||||
|
setBaseUrl(window.location.origin);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleCopy = async (text: string, type: string) => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
setCopySuccess(type);
|
||||||
|
setTimeout(() => setCopySuccess(null), 2000);
|
||||||
|
} catch (err) {
|
||||||
|
// 降级方案:使用document.execCommand
|
||||||
|
const textArea = document.createElement('textarea');
|
||||||
|
textArea.value = text;
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
setCopySuccess(type);
|
||||||
|
setTimeout(() => setCopySuccess(null), 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const configs = [
|
||||||
|
{
|
||||||
|
name: 'TVBox JSON配置',
|
||||||
|
description: '直接返回JSON格式的配置文件,适用于支持在线配置的TVBox应用',
|
||||||
|
url: `${baseUrl}/api/tvbox`,
|
||||||
|
type: 'json'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TVBox Base64配置',
|
||||||
|
description: '返回Base64编码的配置文件,适用于大部分TVBox应用',
|
||||||
|
url: `${baseUrl}/api/tvbox?format=txt`,
|
||||||
|
type: 'base64'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageLayout>
|
||||||
|
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 py-8">
|
||||||
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
{/* 页面标题 */}
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
|
📺 TVBox配置接口
|
||||||
|
</h1>
|
||||||
|
<p className="text-lg text-gray-600 dark:text-gray-300">
|
||||||
|
将KatelyaTV的视频源导入到TVBox应用中使用
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 功能介绍 */}
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
|
||||||
|
🎯 功能特点
|
||||||
|
</h2>
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<span className="text-green-500 text-sm">✅</span>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300">
|
||||||
|
自动同步KatelyaTV的所有视频源
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<span className="text-green-500 text-sm">✅</span>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300">
|
||||||
|
支持TVBox标准JSON格式
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<span className="text-green-500 text-sm">✅</span>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300">
|
||||||
|
内置视频解析接口
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<span className="text-green-500 text-sm">✅</span>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300">
|
||||||
|
支持Base64编码格式
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<span className="text-green-500 text-sm">✅</span>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300">
|
||||||
|
自动CORS跨域支持
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<span className="text-green-500 text-sm">✅</span>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300">
|
||||||
|
配置自动缓存优化
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 配置链接 */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
{configs.map((config) => (
|
||||||
|
<div
|
||||||
|
key={config.type}
|
||||||
|
className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
|
||||||
|
{config.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600 dark:text-gray-300 text-sm">
|
||||||
|
{config.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => handleCopy(config.url, config.type)}
|
||||||
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||||
|
copySuccess === config.type
|
||||||
|
? 'bg-green-500 text-white'
|
||||||
|
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{copySuccess === config.type ? '已复制!' : '复制链接'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
|
||||||
|
<code className="text-sm text-gray-800 dark:text-gray-200 break-all">
|
||||||
|
{config.url}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 使用说明 */}
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mt-8">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
|
||||||
|
📖 使用说明
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-4 text-gray-700 dark:text-gray-300">
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium text-gray-900 dark:text-white mb-2">
|
||||||
|
1. 复制配置链接
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm ml-4">
|
||||||
|
选择上方任一配置链接,点击"复制链接"按钮复制到剪贴板
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium text-gray-900 dark:text-white mb-2">
|
||||||
|
2. 导入到TVBox
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm ml-4">
|
||||||
|
打开TVBox应用 → 设置 → 配置地址 → 粘贴复制的链接 → 确认导入
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium text-gray-900 dark:text-white mb-2">
|
||||||
|
3. 刷新配置
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm ml-4">
|
||||||
|
当KatelyaTV添加新的视频源时,在TVBox中刷新配置即可同步最新源站
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* API参数说明 */}
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mt-8">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
|
||||||
|
🔧 API参数说明
|
||||||
|
</h2>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||||
|
<thead className="bg-gray-50 dark:bg-gray-700">
|
||||||
|
<tr>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||||
|
参数
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||||
|
说明
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||||
|
示例
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||||
|
<tr>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
||||||
|
format
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300">
|
||||||
|
返回格式:json(默认) 或 txt(base64编码)
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300">
|
||||||
|
?format=txt
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 解析接口说明 */}
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mt-8">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
|
||||||
|
🎬 内置视频解析
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
|
KatelyaTV提供内置的视频解析接口,支持主流视频平台:
|
||||||
|
</p>
|
||||||
|
<div className="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
|
||||||
|
<code className="text-sm text-gray-800 dark:text-gray-200">
|
||||||
|
{baseUrl}/api/parse?url=视频地址
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
<p>支持的平台:腾讯视频、爱奇艺、优酷、芒果TV、哔哩哔哩、搜狐视频等</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Clover, Film, Home, Menu, Search, Tv } from 'lucide-react';
|
import { Clover, Film, Home, Menu, Search, Settings, Tv } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||||
import {
|
import {
|
||||||
@@ -138,6 +138,11 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
|
|||||||
label: '综艺',
|
label: '综艺',
|
||||||
href: '/douban?type=show',
|
href: '/douban?type=show',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: Settings,
|
||||||
|
label: 'TVBox配置',
|
||||||
|
href: '/tvbox',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user