From 9c3a98498492029c516502f6118cf0a4d0b20599 Mon Sep 17 00:00:00 2001 From: kuekhaoyang Date: Thu, 2 Apr 2026 20:22:53 +0800 Subject: [PATCH] add VideoTogether website integration --- README.md | 26 ++++++++++++++++++++++++++ app/iptv/layout.tsx | 14 ++++++++++++++ app/player/layout.tsx | 8 +++++++- components/VideoTogetherScript.tsx | 30 ++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 app/iptv/layout.tsx create mode 100644 components/VideoTogetherScript.tsx diff --git a/README.md b/README.md index 4d33cfd..571a1e6 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ - **全屏模式选择**:支持系统全屏和网页全屏两种模式 - **代理播放**:三种代理模式(智能重试、仅直连、总是代理),灵活应对不同网络环境 - **卡顿检测**:自动检测播放卡顿并提示 +- **一起看 (VideoTogether)**:播放器页面内置官方网页集成脚本,可直接创建或加入房间与朋友同步观看 - **键盘快捷键**:空格/K 播放暂停、F 全屏、M 静音、P 画中画、J/L/方向键 快进快退、上下键 音量调节 ### 多源并行搜索 @@ -473,6 +474,28 @@ docker run -d -p 3000:3000 \ > 用户还可以在设置页面的「弹幕 API」区域添加多个 API 端点并选择当前使用的,用户选择的 API 优先于系统默认配置。 +## 一起看 (VideoTogether) 配置 + +播放器页面和 IPTV 页面默认内置 [VideoTogether](https://videotogether.github.io/zh-cn/guide/website.html) 官方网页集成脚本,进入播放页后即可直接使用一起看功能。 + +| 变量名 | 说明 | 默认值 | +|--------|------|--------| +| `VIDEOTOGETHER_ENABLED` | 设为 `false` 时禁用一起看集成 | `true` | +| `VIDEOTOGETHER_SCRIPT_URL` | 自定义 VideoTogether 脚本地址,适合自托管或替换 CDN | `https://fastly.jsdelivr.net/gh/VideoTogether/VideoTogether@latest/release/extension.website.user.js` | +| `VIDEOTOGETHER_SETTING_URL` | 自定义 VideoTogether 设置页地址,对应官方 `window.videoTogetherWebsiteSettingUrl` 接口 | - | + +**示例:** + +```bash +# Docker +docker run -d -p 3000:3000 \ + -e VIDEOTOGETHER_SCRIPT_URL="https://your-domain.example.com/extension.website.user.js" \ + -e VIDEOTOGETHER_SETTING_URL="https://your-domain.example.com/videotogether-settings.html" \ + --name kvideo kuekhaoyang/kvideo:latest +``` + +> 如果部署环境无法稳定访问 jsDelivr,直接自托管 `extension.website.user.js` 并通过 `VIDEOTOGETHER_SCRIPT_URL` 指向自己的地址即可。 + ## IPTV 直播源配置 通过环境变量预设 IPTV 直播源,应用启动时会自动添加到直播源列表中。 @@ -612,6 +635,9 @@ docker run -e PORT=8080 -p 8080:8080 --name kvideo kuekhaoyang/kvideo:latest | `AD_KEYWORDS` / `NEXT_PUBLIC_AD_KEYWORDS` | 广告过滤关键词 | - | | `AD_KEYWORDS_FILE` | 广告关键词文件路径 | - | | `DANMAKU_API_URL` / `NEXT_PUBLIC_DANMAKU_API_URL` | 弹幕聚合 API 地址 | - | +| `VIDEOTOGETHER_ENABLED` | 是否启用 VideoTogether 一起看集成(`false` 时关闭) | `true` | +| `VIDEOTOGETHER_SCRIPT_URL` | VideoTogether 脚本地址 | `https://fastly.jsdelivr.net/gh/VideoTogether/VideoTogether@latest/release/extension.website.user.js` | +| `VIDEOTOGETHER_SETTING_URL` | VideoTogether 设置页地址 | - | | `UPSTASH_REDIS_REST_URL` | Upstash Redis REST URL(跨设备同步:配置、历史、收藏) | - | | `UPSTASH_REDIS_REST_TOKEN` | Upstash Redis REST Token | - | diff --git a/app/iptv/layout.tsx b/app/iptv/layout.tsx new file mode 100644 index 0000000..d5b63c8 --- /dev/null +++ b/app/iptv/layout.tsx @@ -0,0 +1,14 @@ +import { VideoTogetherScript } from '@/components/VideoTogetherScript'; + +export default function IPTVLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> + {children} + + + ); +} diff --git a/app/player/layout.tsx b/app/player/layout.tsx index 2eee97e..fc92ce8 100644 --- a/app/player/layout.tsx +++ b/app/player/layout.tsx @@ -1,4 +1,5 @@ import { Metadata } from 'next'; +import { VideoTogetherScript } from '@/components/VideoTogetherScript'; export const metadata: Metadata = { referrer: 'no-referrer', @@ -9,5 +10,10 @@ export default function PlayerLayout({ }: { children: React.ReactNode; }) { - return children; + return ( + <> + {children} + + + ); } diff --git a/components/VideoTogetherScript.tsx b/components/VideoTogetherScript.tsx new file mode 100644 index 0000000..f027ac5 --- /dev/null +++ b/components/VideoTogetherScript.tsx @@ -0,0 +1,30 @@ +import Script from 'next/script'; + +const DEFAULT_VIDEOTOGETHER_SCRIPT_URL = + 'https://fastly.jsdelivr.net/gh/VideoTogether/VideoTogether@latest/release/extension.website.user.js'; + +export function VideoTogetherScript() { + if (process.env.VIDEOTOGETHER_ENABLED === 'false') { + return null; + } + + const scriptUrl = process.env.VIDEOTOGETHER_SCRIPT_URL?.trim() || DEFAULT_VIDEOTOGETHER_SCRIPT_URL; + const settingUrl = process.env.VIDEOTOGETHER_SETTING_URL?.trim(); + + return ( + <> + {settingUrl ? ( +