Profile
Back to NewsBack
GitHub Trending 4 min
Reader Mode
Zero-wyc/ZViewer: 多人一起观影、听歌、语音聊天和屏幕分享平台  |  Multi-person simultaneous movie viewing, chasing and Self-hosted watch-together platform

Zero-wyc/ZViewer: 多人一起观影、听歌、语音聊天和屏幕分享平台 | Multi-person simultaneous movie viewing, chasing and Self-hosted watch-together platform

6 hours ago

ZViewer

多人一起观影、听歌、语音聊天和屏幕分享平台。

English | 中文

MIT Stars Release Contributors Repo Size Last Commit Top Language Telegram


Telegram QQ


项目文档:docx.zviewer.zero251.xyz

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ | | | | | | | | | |

Star History

Star History Chart

浏览器要求

建议使用 Chrome / Edge 等内核 130+ 的 Chromium 浏览器。Safari 与 Firefox 对 MSE / MKV 解码支持不完整,可能出现卡顿、无法解码、字幕异常。

PS:另有正处于开发状态的Zviewer手机端 | 并由FredQin开发

目录

- Windows 桌面版(GUI) - 单文件版(推荐) - 交互菜单 - 源码 - 端口 - 环境变量 - 语音传输

功能一览

  • 一起看:多人同步观影,房主控播,观众可申请暂停 / 快进
  • 视频源:Bilibili(分 P、清晰度、大会员画质)、MP4 直链、WebDAV / FTP / OpenList、Emby / Jellyfin、服务器本地目录
  • 格式兼容:MKV / AVI / TS / WMV、DTS / AC3 / EAC3 等浏览器不原生支持的格式
  • 播放模式:CLI 本地代理(高画质、不耗服务器流量)、服务器 DASH 转发、MP4 直链(最高 720P),按需切换
  • 弹幕与字幕:B 站官方 / DandanPlay / 本地 XML·JSON(可多轨);SRT / ASS / SSA / VTT / SMI / SUB
  • 互动与推流:评论、语音聊天(UDP / TCP 通道自动择优,无需手动切换)、WebRTC 屏幕共享、OBS 推流(RTMP 收流与 HTTP-FLV 分发由后端进程内提供,不需要额外装媒体服务器)
  • 一起听:网易云音乐 × 哔哩哔哩,歌单自动补位推荐、双收藏夹一键收藏、MV 歌词页背景(需自备一个 NCM API 服务,见「部署配置 → 推流与一起听」)
  • 个性化:Material You 动态主题、自定义主题色与背景、玻璃拟态、弹幕字幕字号独立调节
  • 运维:一键自动更新,Docker / 单文件版开箱即用

ZViewerCLI

ZViewerCLI(Go)是运行在用户本地的代理程序:用你自己的 Bilibili Cookie 在本地解析并代理视频流,让房间内所有人共享大会员画质,且几乎不消耗服务器带宽。适用于既要高画质、又想节省服务器流量的场景(服务器 DASH 转发会大量消耗带宽)。

安装与启动

首次启动自动创建 ROOT 管理员:用户名 root,密码 root。安装后请立即修改密码。

Windows 桌面版(GUI)(Windows用户推荐)

Release 中的 ZViewer-Windows-<版本>-x64.zip 是原生桌面控制台(Go + 系统 WebView2),用于管理本机服务、证书、FRP 穿透与高画质代理:

  • 解压后双击 ZViewerDesktop.exe 即可查看控制台界面,并根据其中提示进行启动
  • 内置高画质代理 CLI(runtime/zviewer-cli-windows-amd64.exe
  • 构建与调试方式见 desktop/README.md。

单文件版

从 4.x(Node 版)升级到 5.x(Go 版):更新完成后第一次启动会自动清理 Node 时代遗留的文件
(zviewer-frontend.exe、zviewer-cert、node_modules/、backend/、config/media 里的推流切片),
启动日志会打印一条 [cleanup] 已清理 v4 遗留文件 …。config/ 下的数据库、证书、上传文件不受影响;
源码目录(含 .git 或 frontend/src)不会做目录级清理。

从 Releases 下载对应平台压缩包,解压后:

  • Windows:双击 start.bat,进入交互菜单
  • Linux:执行 ./start.sh(不带参数),进入交互菜单
在菜单中选择 1 启动服务,完成后访问 http://localhost:3333。

也可以跳过菜单直接带命令执行,如 start.bat start / ./start.sh start;完整命令见 start.bat help。

交互菜单

1) 启动服务              2) 仅启动后端
  3) 停止服务              4) 重启服务
  5) 查看状态              6) 查看日志
  7) 一键签发 SSL 证书     8) HTTPS 启动(自动签发证书)
  0) 退出

| 编号 | 功能 | 说明 | | -- | -------- | ------------------------------------------------------ | | 1 | 启动服务 | HTTP 启动,端口取 .env 的 PORT(默认 3333) | | 2 | 仅启动后端 | 先选 HTTP / HTTPS;跳过前端产物校验,适合前后端分离调试 | | 3 | 停止服务 | 按记录的 PID 停止,端口被占用时兜底清理 | | 4 | 重启服务 | 停止后重新启动 | | 5 | 查看状态 | 端口监听、进程 PID、程序与证书是否齐全 | | 6 | 查看日志 | 输出 log/backend.log 末 50 行 | | 7 | 签发 SSL 证书 | 选 localhost 自签,或公网域名 / 公网 IP 申请 Let's Encrypt 证书(需已解析且 80 端口可达;内网 IP 自动回退自签) | | 8 | HTTPS 启动 | 签发证书后以 HTTPS 启动 | | 0 | 退出 | 只关闭菜单窗口,后台服务继续运行;需要停止请选 3 |

每次操作结束后按回车返回菜单。
不建议使用命令行自动签发正式证书:菜单 7 / 8 的 Let's Encrypt 签发适合测试,续期、验证失败排查都要自己盯。正式环境建议把域名交给 Cloudflare、ESA、EdgeOne 等支持自动签发与续期的边缘服务,或使用 宝塔、1Panel 等面板统一申请续期,再反向代理到 ZViewer 的 3333 端口。

源码

后端是 Go(backend-go/,需要 Go 1.26+),前端是 React + Vite。

npm install             # 安装前端依赖
npm run db:init         # 首次:建库并创建初始管理员 root / root
npm run build           # 生产构建(Go 后端 + 前端)
npm start               # 启动

初始管理员账号和密码为 root / root,登录后请修改密码。已有数据库中的账号密码保持不变。

要改代码,看 DEVELOPMENT.md:环境要求、目录职责、热重载原理、回归脚本、数据库改字段的完整步骤都在那里。

npm run build 会先把 Go 后端编译成 dist/zviewer-backend(.exe),再构建前端产物; npm start 直接拉起该二进制(日志写在 log/backend.log)。

开发模式(Go 后端 + Vite HMR,前端默认把 /api 代理到 3333):

npm run dev             # 同时起后端与前端
npm run dev:backend     # 只起 Go 后端(改 .go 保存即重启)
npm run dev:frontend    # 只起前端

npm run lint:go # gofmt + go vet npm run test:go # go test ./... npm run test:regression # 全部契约对拍与冒烟(13 个纯 Go 脚本,约 2 分钟)

后端热重载由 scripts/dev-backend.js 提供(不需要装 air 之类的外部工具): 监听 backend-go//.{go,mod,sum},先编译到临时产物、成功之后才换进程*, 所以改到一半带着语法错误保存时旧进程仍在服务,修好保存即自动恢复。 不想要热重载时用 npm run dev:backend:once。

或使用脚本完成依赖安装、构建与启动:

.\start-prod.bat start    # Windows
./start-prod.sh start     # Linux / macOS

支持命令:start / backend / stop / restart / status / logs / build / cert / https / help。

端口

| 端口 | 用途 | | -------- | ------------------------------------------------------------ | | 3333/tcp | 统一入口:HTTP/HTTPS API、WebSocket、前端页面、/live FLV 代理 | | 3333/udp | LiveKit 语音聊天 | | 3334/tcp | OBS 推流 | | 3335/tcp | HTTP-FLV 拉流(内部,经 3333 的 /live 代理,无需对外开放) | | 3336/tcp | LiveKit HTTP/信令(内部,经 3333 的 /rtc 反代,无需对外开放) | | 3337/tcp | 语音 ICE/TCP 直连(默认开启,UDP 被拦时浏览器自动改走此端口) | | 3338/tcp | 语音 TURN/TLS 中继(可选,配齐证书三项即启用) |

Docker 部署

v4.3.6 以下版本使用 Docker 部署的用户:请不要通过管理面板「基础设置 → 一键更新」升级,请按最新 docker compose 重新部署。
docker run -d \
  --name zviewer \
  --restart unless-stopped \
  -p 3333:3333 \
  -p 3333:3333/udp \
  -p 3334:3334 \
  -p 3337:3337 \
  -p 3338:3338 \
  -v zviewer-data:/app/config \
  zerowyc0721/zviewer:latest

或 docker compose:

services:
  zviewer:
    image: zerowyc0721/zviewer:latest
    ports:
      - "3333:3333"      # API + WebSocket + 前端页面 + /live FLV 代理 + /rtc 语音信令反代
      - "3333:3333/udp"  # LiveKit WebRTC 媒体传输
      - "3334:3334"      # RTMP 推流(OBS)
      - "3337:3337"      # 语音 ICE/TCP 直连(默认开启,UDP 被拦时浏览器自动改走)
      - "3338:3338"      # 语音 TURN/TLS 中继(可选,配齐证书三项即启用)
    volumes:
      - zviewer-data:/app/config
    restart: unless-stopped
  • 请将 zviewer-data 替换为实际数据目录
  • 镜像标签:latest 与 X.Y 只在发布正式版本(推送 vX.Y.Z tag)时更新,X.Y.Z 为固定版本;dev 跟随 main 分支最新构建,可能不稳定,仅供尝鲜
  • 镜像以 HTTP 模式启动,HTTPS 需自行配置:推荐 Cloudflare、ESA、EdgeOne 等边缘服务,或宝塔、1Panel 等面板自动签发并续期,再反代到 3333(不建议用命令行自助签发正式证书)
  • /app/config 含数据库(dev.sqlite)、证书(ssl/)、上传文件(uploads/);media/ 是预留目录,当前推流走内存转封装、不落盘
  • 一起听需要一个额外的 NCM API 服务(见「部署配置 → 推流与一起听」):用仓库里的 compose 加 --profile music 可一并拉起;只用 docker run 时自己起一个并把 NCM_API_BASE 指过去
  • Docker 模式下仍可使用网页更新

部署配置

环境变量

| 变量 | 说明 | 默认值 | | ------------------------ | ------------------------------------- | ----------------------- | | PORT | 后端端口 | 3333 | | HOST | 监听地址 | 空(双栈) | | CONFIG_DIR | 数据根目录 | /config | | DATABASE_URL | SQLite 文件路径(相对 CONFIG_DIR 或绝对路径) | /dev.sqlite | | SQLITE_WAL | 日志模式;设 0 退回 journal_mode=delete | 启用 WAL | | CORS_ORIGIN | CORS 来源,逗号分隔 | * | | JWT_ACCESS_SECRET | Access 密钥(生产必须修改) | — | | JWT_REFRESH_SECRET | Refresh 密钥(生产必须修改) | — | | RTMP_PORT | RTMP 推流端口(后端进程内监听,OBS 连这个) | 3334 | | HTTP_FLV_PORT | FLV 拉流端口(内部,只经 3333 的 /live 反代访问) | 3335 | | NMS_BIND | 内置媒体服务的监听地址 | 0.0.0.0 | | NMS_HTTP_FLV_URL | 指向外部媒体服务(SRS / node-media-server),设置后不再内嵌 | 空(内嵌) | | NCM_API_BASE | 一起听用的网易云 API 地址(外部服务) | http://127.0.0.1:36530 | | LIVEKIT_EXTERNAL | 1 时使用外置 LiveKit,跳过内嵌 | 0 | | LIVEKIT_NODE_IP | ICE 广播地址;NAT 复杂或无出网环境可手动指定公网 IP | 空(自动 STUN) | | LIVEKIT_BIND | LiveKit HTTP/信令监听地址(仅影响 3336,媒体端口不受影响) | 空(双栈全地址) | | LIVEKIT_RTC_TCP_PORT | ICE/TCP 直连端口(默认开启;设 0 关闭该监听,仅保留 UDP) | 3337 | | LIVEKIT_RTC_ADVERTISE_INTERNAL_IP | 内外网双 IP 候选(advertise_internal_ip),false 关闭 | true | | LIVEKIT_RTC_SKIP_EXTERNAL_IP_VALIDATION | 跳过外部 IP 自 ping 校验(NAT 不允许自 ping 时开启) | 未设置 | | LIVEKIT_TURN_TLS_PORT | TURN/TLS 中继端口(与 3337 分开,可同时开启) | 3338 | | LIVEKIT_TURN_DOMAIN | TURN/TLS 域名 | — | | LIVEKIT_TURN_CERT | TURN TLS 证书路径 | — | | LIVEKIT_TURN_KEY | TURN TLS 私钥路径 | — | | EMBY_CLIENT_NAME | Emby / Jellyfin 客户端标识名(X-Emby-Authorization);部分服务器限制客户端名时改这里 | ZViewer |

推流与一起听

OBS 推流开箱可用:后端进程内自带 RTMP 收流(3334)与 HTTP-FLV 分发(3335), 不依赖 node-media-server / SRS,也不需要额外容器。OBS 里服务器填 rtmp://<你的地址>:3334/live、推流码填房间页面给的码即可。想改用外部媒体服务 (集群、已有 SRS)时设 NMS_HTTP_FLV_URL,此时后端不再监听这两个端口。

一起听需要一个外部的网易云 API 服务。后端只做 /api/music/ncm/* 的转发, 不含网易云协议实现(那部分不是 Go 重写的范围)。任选一种方式启动:

# 1) 和 zviewer 一起起(compose 内置可选服务)
docker compose -f docker-compose.linux-single.yml --profile music up -d

2) 单独起社区镜像,映射到默认的 36530

docker run -d --name zviewer-ncm -p 127.0.0.1:36530:3000 \ binaryify/netease_cloud_music_api:latest

3) 指向任何已部署的 NCM API(含增强版)

NCM_API_BASE=http://192.168.1.20:36530 ./start-prod.sh start

没跑这个服务时,只有音乐相关接口返回 503 NCM_UNAVAILABLE(前端显示音乐服务不可用), 观影、房间、弹幕、语音、推流全部照常。后端启动日志会打印当前的 NCM_API_BASE。

语音传输

  • 通道不设开关:服务端同时发布 3333/udp(低延迟直连)与 3337/tcp(ICE/TCP 直连)两条候选,浏览器 ICE 自动择优——UDP 直连 > TCP 直连 > TURN 中继;UDP 被运营商 / 企业防火墙或代理拦截时自动改走 3337,管理端不提供也不需要切换
  • 想彻底关闭 3337 监听(只开放 UDP 端口)时设 LIVEKIT_RTC_TCP_PORT=0。必须显式写 0——留空或删除会让 LiveKit 回落到默认 7881,而 7881 未做端口映射,客户端只会拿到必然连不上的候选
  • 内外网双通:默认开启 advertise_internal_ip,同时广播映射后的外网 IP 与本地网卡 IP,内网与外网用户各自选中可达的那条。bridge 网络下本地 IP 是容器 172.x,对局域网客户端不可达;需要内网直连时改用 host 网络(去掉 compose 的 ports 段、加 network_mode: host),并用 LIVEKIT_CONFIG 的 rtc.interfaces.includes: [eth0] 限定网卡。NAT 不允许自 ping 时再加 LIVEKIT_RTC_SKIP_EXTERNAL_IP_VALIDATION=true
  • 配齐 LIVEKIT_TURN_* 三项时,还会在 3338/tcp 开启 TURN/TLS 中继(需正式证书,穿透代理/多层 NAT 能力最强)。3337 与 3338 是两条独立通道,可并存,客户端自动择优:UDP 直连 > TCP 直连 > TURN 中继
  • 纯内网 / 特殊 NAT 环境 STUN 探测失败时,用 LIVEKIT_NODE_IP 指定服务器公网 IP
  • 排障:语音面板在媒体通道未建立时显示「媒体连接中…」,超过 10 秒会给出网络提示。进房成功但没声音,通常是媒体通道没通(代理 TUN 或防火墙吞掉 UDP),可先关掉代理 / VPN 再试
内网与外网同时直连(推荐 host 网络)

bridge 网络下只有映射后的地址可达,局域网用户常因路由器不支持 NAT 回环(hairpin)而连不上。需要两侧都直连时改用 host 网络,并把网卡限死:

services:
  zviewer:
    image: zerowyc0721/zviewer:latest
    network_mode: host          # 去掉原 ports 段:3333、3333/udp、3334、3337、3338 直接监听宿主
    environment:
      LIVEKIT_BIND: "127.0.0.1"                          # 信令只绑本机(外部统一走 3333 反代)
      LIVEKIT_RTC_USE_EXTERNAL_IP: "true"
      LIVEKIT_RTC_ADVERTISE_INTERNAL_IP: "true"
      LIVEKIT_RTC_SKIP_EXTERNAL_IP_VALIDATION: "true"   # NAT 不允许自 ping 时再加
      LIVEKIT_CONFIG: |-
        rtc:
          interfaces:
            includes: [eth0]
    volumes:
      - ./config:/app/config
    restart: unless-stopped

host 网络没有端口映射,LIVEKIT_BIND: "127.0.0.1" 用来把 LiveKit 自身的 HTTP/信令端口(3336)收回本机,外部只经 3333 的 /rtc 反代进入。它只作用于信令:媒体端口(3333/udp、3337/tcp、3338/tcp)仍监听全部网卡,不影响内外网直连。

interfaces.includes 用于排除 docker0、veth、tailscale 等接口,避免广播一批必然不可达的候选。网卡名必须与宿主机实际一致(ip -br addr 查看,云主机常见 eth0 / ens18 / enp3s0);写错会枚举不到任何 ICE 候选,语音直接连不上——拿不准就先删掉这段,让 LiveKit 自动枚举。

  • 需要 TURN/TLS 中继时,配置 LIVEKIT_TURN_DOMAIN / LIVEKIT_TURN_CERT / LIVEKIT_TURN_KEY 三项即可(compose 已含 3338:3338 映射,需放行 3338/tcp)

Windows 桌面控制台(可选)

Windows 用户可构建原生目录 GUI(Go + 系统 WebView2,无需安装运行时,也不依赖 Electron),双击分发目录唯一的 ZViewerDesktop.exe 打开控制台,运行包放在同级 runtime/`。控制台提供系统托盘、本机 HTTPS、自建 FRP、可保存的监听端口,以及直接在窗口内配置和扫码登录的 ZViewerCLI 高画质代理。构建和使用步骤见 desktop/README.md,现有服务端网页与命令行源码保持兼容。

Chat with me