☰
用 PartyKit 构建 YouTube Watch Party:全屏视频与实时聊天的多人观影实战
2026/10/14 16:21:57 网站建设 项目流程
  • 后端

【免费下载链接】partykit

PartyKit simplifies developing multiplayer applications

项目地址:https://gitcode.com/gh_mirrors/pa/partykit
点击查看免费下载

本文围绕 PartyKit 官方示例文档 youtube-watch-party 展开:它演示了一个"带实时聊天的 YouTube 观影派对"——参与者共享同一段 YouTube 直播画面,同时在一个不落盘的临时聊天室里实时交谈,营造出"一起在线看视频"的临场感。读完本文,你将掌握用 PartyKit 的 Remix 起步模板把"全栈网站 + 多人实时后端"整体托管到partykit.dev域名下的方法,理解 Party.Server 服务端接口与 PartySocket 客户端的配合方式,并能够亲手复刻一个类似的 watch party 应用。

示例速览:一个"一起看视频"的实时房间

在 PartyKit 官方文档的 App examples 列表 中,youtube-watch-party与 The Namib Desert watering hole livestream 同属一批真实落地、可在线体验的多人应用。示例文档对其定位只有一句话:A YouTube watch party with a live chat——一个由 Matt Webb(PartyKit 驻场发明家)构建的 YouTube 观影派对应用,核心价值是"把观看心爱 YouTube 视频的快乐分享给朋友们"。

这个示例值得关注的地方在于:它并不是一个演示用的空壳,而是一个真实在线的应用(示例文档给出的 live demo 为waterhole.genmon.partykit.dev),背后还有作者本人撰写的官方博客 single-serving-waterhole 详细拆解实现思路。也就是说,"带实时聊天的 YouTube watch party"是一个经过实战检验、有完整工程轨迹的 PartyKit 应用形态。

从作者博客拆解:这个应用到底做了什么

按官方博客 single-serving-waterhole 的描述,该应用的原始素材是 YouTube 上 NamibiaCam 的纳米布沙漠水坑 24 小时直播(能实时看到跳羚、疣猪、鸵鸟甚至长颈鹿喝水)。作者平时就开着这扇"沙漠之窗"工作,于是把它做成了一个多人应用。这个应用由几个关键设计组成:

  • 全屏(full bleed)视频:视频画面铺满整个视口,浏览器窗口无论怎么缩放,直播画面都会自动撑满屏幕,让页面"看起来不像一个网站,而像通往另一个地方的传送门"。
  • 临时聊天(ephemeral chat):聊天消息只在实时共享,不落盘、不保留,慢慢滚动消逝。作者认为这种"易逝"恰好能给出足够的在场感(presence)。
  • "6 here"在线徽章:右下角的小徽章显示当前在线人数,作者表示这是他在许多 PartyKit 项目里都会放的组件,用来快速确认 WebSocket 是否存活。

从工程实现上看,这个应用有两个值得注意的事实(均出自博客原文):其一是整个站点完全托管在 PartyKit 上,URL 形态为waterhole.genmon.partykit.dev,也就是说 PartyKit 同时承担了静态资源托管与多人实时后端;其二是作者通过PartyKit 的 Remix starter kit起步,套用模板后加入自己的代码直接部署,一句话概括就是"PartyKit 处理多人(聊天)部分,同时也把资源托管了"。

技术底座:一套代码托管"全栈 + 多人"

一条命令起步:Remix starter

文档与博客中提到的 Remix starter 是 PartyKit 官方为 Remix(一个基于 React 的全栈 Web 框架)准备的起步模板。官方博客 introducing-starter-kits 记录了它的初始化命令:

npx create-remix@latest --template partykit/remix-starter

这条命令会创建一个把整个 Remix 应用托管在 PartyKit 平台上的工程。底层桥接由仓库内的partymix包完成,packages/partymix/README.md 说明了其定位:一个用于把 Remix 应用部署到 PartyKit 服务器的适配器。在既有 Remix 工程中手动接入时,步骤是:

npm install partykit partymix

随后定义 PartyKit 服务器(将 Remix 的请求处理器挂到onFetch上):

// src/server.js import { createRequestHandler } from "partymix"; import * as build from "@remix-run/dev/server-build"; const handleRequest = createRequestHandler({ build }); export default class MyRemix { static onFetch(request, lobby, ctx) { return handleRequest(request, lobby, ctx); } }

并在remix.config.js中为 PartyKit 运行时(基于workerd)做相应配置,核心是serverConditions: ["partykit", "workerd", "worker", "browser"]与serverPlatform: "neutral"等。仓库内的 examples/remix/app/server.ts 就是这一模式的 TypeScript 版本实例,可以直接对照阅读。

partykit.json:一个文件描述整个项目

接好适配器后,工程根目录只需一份 partykit.json 就能描述整个项目。对照 PartyKit 配置参考,本示例涉及的字段及其含义如下:

字段作用本示例取值
name项目名,同时决定线上 URL(https://<name>.<user>.partykit.dev)partymix-example
main服务端入口文件,默认导出 Party.Server 实现dist/index.js(指向构建产物)
serve托管静态资源或静态站点的目录public
compatibilityDate运行时兼容日期2023-09-28

其余常用配置还包括port(本地开发端口,默认1999)与persist(开发期 Party 存储持久化路径,默认.partykit/state)。配置参考文档 partykit-configuration.md 对每个字段都有完整说明,如需托管多路由页面或自定义构建流程,可进一步参考其中parties与build字段的用法。

Party.Server:服务端编程接口

PartyKit 服务器是一个实现Party.Server接口的 TypeScript 模块,Party.Server API 参考 给出了最小骨架:

// server.ts import type * as Party from "partykit/server"; export default class Server implements Party.Server { constructor(readonly room: Party.Room) { // ... } }

构造函数接收的Party.Room是当前"房间"的句柄,通过它可以访问存储、连接列表、房间 id 等资源。对本示例而言,最关键的机制来自 how-partykit-works.md 中描述的房间路由模型:每个 Party 实例背后是一个 Cloudflare Durable Object,连接 URL 形如https://${project}.${user}.partykit.dev/parties/main/${id};使用相同id的连接总是被路由到同一个房间实例,使用新的id则自动创建新实例。对 watch party 来说,"同一段视频的所有观众"天然对应一个房间id,客户端只需带上同一个id连接,就会自动聚到同一个实时房间中,无需任何额外编排。

实时聊天的实现思路

文档虽未给出具体代码,但结合官方 Building a WebSocket server 指南 与 Party.Server API 中的标准模式,可以完整还原"临时聊天"的服务端实现思路:

  • 临时聊天的语义:博客明确说明消息"不被保留,只在实时共享"。因此服务端无需把消息写入持久化存储(room.storage),只需在onMessage中把消息转发给房间内的其他连接即可。这正是 PartyKit 的典型"广播型"房间用法:
export default class WebSocketServer implements Party.Server { constructor(readonly room: Party.Room) {} onMessage(message: string, sender: Party.Connection) { // 把消息转发给所有其他已连接客户端 for (const conn of this.room.getConnections()) { if (conn.id !== sender.id) { conn.send(`${sender.id} says: ${message}`); } } } }
  • 在线人数徽章:onConnect(新连接建立)与onClose(连接关闭)两个生命周期钩子(参考 partyserver-api.md)天然适合维护当前在线人数——连接建立时计数加一、断开时减一,并通过广播把最新数字推给所有客户端,这就是"6 here"徽章背后最朴素的 WebSocket 存活检查逻辑。
  • 不落盘的选择:正因为聊天是"易逝"的,这个应用甚至可以完全省略持久化环节,让服务端成为一个纯粹的内存转发器。需要说明的是,这属于从博客描述可以推断的实现取舍,而非文档给出的唯一方案;若希望聊天支持回放,使用 persisting state into storage 指南 中room.storage的相关 API 即可。

客户端接入:PartySocket 与 react-player

在浏览器端,PartyKit 提供了官方客户端库PartySocket,PartySocket API 参考 给出的核心用法如下:

import PartySocket from "partysocket"; const ws = new PartySocket({ host: "project-name.username.partykit.dev", // 开发时为 localhost:1999 room: "my-room", // 同一个 room 会路由到同一个 Party 实例 // party: "main", // 不传则连接 partykit.json 中 main 定义的 party // query: async () => ({ token: await getAuthToken() }) // 可选的身份参数 }); ws.send("Hello everyone"); ws.addEventListener("message", (e) => { console.log(e.data); });

React 工程还可以直接使用partysocket/react导出的usePartySockethook,在组件内以onOpen/onMessage/onClose等生命周期回调组织逻辑。此外,PartySocket 支持连接关闭后自动重连、缓冲未发送消息等能力,适合 watch party 这类需要长时间挂机的场景。

至于视频本体,作者在博客中明确提到使用了 react-player——一个包装 YouTube 播放器等多种播放源的 React 组件。结合示例的"全屏铺满视口"设计,客户端的职责划分就很清晰了:视频播放完全由 react-player 在本地完成,PartySocket 只负责聊天与在场状态的实时同步,两者互不阻塞。

本地开发与部署

配合 PartyKit CLI 参考,一套完整的 watch party 开发闭环如下:

# 安装 CLI npm install partykit@latest # 在现有 npm 工程中初始化(生成 partykit.json 与示例 client.ts / server.ts) npx partykit init # 本地开发:监听代码变更并自动重启,默认端口 1999 npx partykit dev # 登录平台(首次 deploy 时也会自动触发) npx partykit login # 部署到线上,生成 https://<name>.<user>.partykit.dev 地址 npx partykit deploy

对于 Remix 模板工程,仓库内 examples/remix/package.json 展示了把 Remix 构建与 PartyKit 命令串起来的标准脚本:

{ "scripts": { "build": "remix build", "deploy": "npm run build && partykit deploy", "start": "remix dev --manual -c \"partykit dev\"" } }

调试线上问题时,可用npx partykit tail实时查看项目日志,或npx partykit list/npx partykit delete管理已发布的项目。

扩展方向:把 Watch Party 升级为共享点唱机

作者在博客结尾给出了一个颇具启发性的扩展设想:因为 react-player 本身提供了远超当前用法的播放控制能力,只需用 PartyKit 同步播放状态(当前视频 URL、播放/暂停、进度)到所有客户端,就能轻松把 watch party 变成共享点唱机(shared jukebox)。这一点在仓库内也能找到同构的实践范式——作者的另一篇博客 partycore-140bpm-with-tonejs 记录了用 PartyKit 的 Remix 模板构建多人鼓机序列器的过程:共享数据通过y-partykit(PartyKit 的原生 Yjs 后端)在客户端与服务端间同步,客户端则把复杂交互封装进自定义 hook(如useSequencer)中。参照这一模式,"共享播放器状态"完全可以落在 Yjs 共享文档上,让进度条、歌单与播放控制像鼓机步进一样实时同步。

继续探索

  • 示例文档:youtube-watch-party.md(本文主体)、the-namib-desert-watering-hole-livestream.md
  • 作者博客:single-serving-waterhole.md
  • 平台原理:how-partykit-works.md
  • API 参考:Party.Server、PartySocket、partykit.json 配置、PartyKit CLI
  • 上手教程:Building a WebSocket server、Remix starter、partymix 适配器说明
  • 仓库实例:examples/remix(Remix + PartyKit 全栈模板)、examples/remix/partykit.json、examples/remix/app/server.ts
  • 后端

【免费下载链接】partykit

PartyKit simplifies developing multiplayer applications

项目地址:https://gitcode.com/gh_mirrors/pa/partykit
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询