diff --git a/README.md b/README.md index 2966557..b17c8c7 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ - **☁️ 多平台支持**:Vercel、Cloudflare Pages、传统服务器全兼容 - **🔧 灵活配置**:支持自定义资源站、代理设置、主题配置 - **📱 PWA 支持**:可安装为桌面/手机应用,离线缓存 +- **📺 TVBox 兼容**:支持 TVBox 配置接口,可导入到各种电视盒子应用 ### 🎨 用户体验 @@ -63,6 +64,32 @@ | 代码质量 | ESLint · Prettier · Jest · Husky | | 部署 | 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) + ## 🚀 部署教程 > **💡 推荐方案**: diff --git a/docs/TVBOX.md b/docs/TVBOX.md new file mode 100644 index 0000000..29e647e --- /dev/null +++ b/docs/TVBOX.md @@ -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 及其衍生应用。_ diff --git a/src/app/api/parse/route.ts b/src/app/api/parse/route.ts new file mode 100644 index 0000000..8bd992f --- /dev/null +++ b/src/app/api/parse/route.ts @@ -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 = ` + + + + + 视频播放 + + + + + +`; + 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', + } + }); +} diff --git a/src/app/api/tvbox/route.ts b/src/app/api/tvbox/route.ts new file mode 100644 index 0000000..61232f8 --- /dev/null +++ b/src/app/api/tvbox/route.ts @@ -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; + header?: Record; + }>; // 解析源 + flags?: string[]; // 播放标识 + ijk?: Record; // 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', + } + }); +} diff --git a/src/app/tvbox/page.tsx b/src/app/tvbox/page.tsx new file mode 100644 index 0000000..20faee9 --- /dev/null +++ b/src/app/tvbox/page.tsx @@ -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(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 ( + +
+
+ {/* 页面标题 */} +
+

+ 📺 TVBox配置接口 +

+

+ 将KatelyaTV的视频源导入到TVBox应用中使用 +

+
+ + {/* 功能介绍 */} +
+

+ 🎯 功能特点 +

+
+
+
+ + + 自动同步KatelyaTV的所有视频源 + +
+
+ + + 支持TVBox标准JSON格式 + +
+
+ + + 内置视频解析接口 + +
+
+
+
+ + + 支持Base64编码格式 + +
+
+ + + 自动CORS跨域支持 + +
+
+ + + 配置自动缓存优化 + +
+
+
+
+ + {/* 配置链接 */} +
+ {configs.map((config) => ( +
+
+
+

+ {config.name} +

+

+ {config.description} +

+
+ +
+ +
+ + {config.url} + +
+
+ ))} +
+ + {/* 使用说明 */} +
+

+ 📖 使用说明 +

+
+
+

+ 1. 复制配置链接 +

+

+ 选择上方任一配置链接,点击"复制链接"按钮复制到剪贴板 +

+
+ +
+

+ 2. 导入到TVBox +

+

+ 打开TVBox应用 → 设置 → 配置地址 → 粘贴复制的链接 → 确认导入 +

+
+ +
+

+ 3. 刷新配置 +

+

+ 当KatelyaTV添加新的视频源时,在TVBox中刷新配置即可同步最新源站 +

+
+
+
+ + {/* API参数说明 */} +
+

+ 🔧 API参数说明 +

+
+ + + + + + + + + + + + + + + +
+ 参数 + + 说明 + + 示例 +
+ format + + 返回格式:json(默认) 或 txt(base64编码) + + ?format=txt +
+
+
+ + {/* 解析接口说明 */} +
+

+ 🎬 内置视频解析 +

+

+ KatelyaTV提供内置的视频解析接口,支持主流视频平台: +

+
+ + {baseUrl}/api/parse?url=视频地址 + +
+
+

支持的平台:腾讯视频、爱奇艺、优酷、芒果TV、哔哩哔哩、搜狐视频等

+
+
+
+
+
+ ); +} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index b5b7c5d..d559ead 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,6 +1,6 @@ '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 { usePathname, useRouter, useSearchParams } from 'next/navigation'; import { @@ -138,6 +138,11 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { label: '综艺', href: '/douban?type=show', }, + { + icon: Settings, + label: 'TVBox配置', + href: '/tvbox', + }, ]; return (