Homepage 集成 Tautulli(Plex)Widget:实时监控播放流的完整配置指南
【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage
本文以 Homepage 开源项目中的 Tautulli 服务 Widget 文档 为主体,介绍如何在 Homepage 中接入 Tautulli(Plex 媒体服务器监控工具),在个人主页/应用仪表板上实时展示当前活跃的 Plex 播放流。读完本文,你将掌握 API Key 获取、YAML 配置的每个参数含义与默认值,并能结合源码理解该 Widget 的底层请求链路、渲染逻辑与排错方法。
一、功能概述:为什么在 Homepage 中集成 Tautulli
Tautulli(原名 PlexPy)是 Plex 生态中流行的第三方监控与统计工具,能够记录播放历史、追踪活跃会话、统计带宽与转码情况。Homepage 的 Tautulli Widget 定位十分聚焦:实时展示当前正在进行的 Plex 播放流(active streams),包括正在播放的媒体标题、观看进度、播放/暂停状态、视频与音频的决策(Direct Play / Copy / Transcode)等信息,让你在打开个人主页时即可一眼掌握媒体服务器的实时负载。
从官方文档的定位描述看(docs/widgets/services/plex-tautulli.md):
Provides detailed information about currently active streams.
即该 Widget 只围绕"当前活跃播放流"这一核心场景,不承担历史统计等 Tautulli 的其他能力,配置上也因此非常简单——没有可配置的显示字段(Allowed fields: no configurable fields for this widget),只通过少量布尔选项调整展示细节。
二、前置条件:获取 Tautulli API Key
Homepage 通过 Tautulli 的 HTTP API 拉取实时数据,因此需要 API Key 才能访问。获取方式如下:
- 登录 Tautulli 的 Web 界面;
- 进入
Settings > Web Interface > API; - 在该页面中复制 API Key(一串较长的字符串)。
该 Key 将作为配置项key填入 Homepage 的 Widget 配置中。值得注意的是,Tautulli 的 API 默认要求请求携带apikey参数(详见下文源码分析),Homepage 正是以该参数形式透传认证信息的。
三、Widget 配置指南
3.1 最小可运行配置
在 Homepage 的services.yaml(或对应的分组配置)中新增一个服务条目,将widget.type设为tautulli,并填入 Tautulli 服务的 URL 与 API Key:
- Tautulli: icon: tautulli.png href: http://tautulli.host.or.ip:port description: Plex 监控 widget: type: tautulli url: http://tautulli.host.or.ip:port key: apikeyapikeyapikeyapikeyapikeyurl:Tautulli 服务地址,格式为http://tautulli.host.or.ip:port,需与 Tautulli 实际监听端口(默认 8181)一致;key:上一节获取的 API Key。
3.2 完整参数与默认值
根据 docs/widgets/services/plex-tautulli.md 中的官方示例,该 Widget 的全部可用参数如下:
widget: type: tautulli url: http://tautulli.host.or.ip:port key: apikeyapikeyapikeyapikeyapikey enableUser: true # optional, defaults to false showEpisodeNumber: true # optional, defaults to false expandOneStreamToTwoRows: false # optional, defaults to true参数说明:
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
type | string | 必填 | 固定为tautulli,用于加载对应 Widget 实现 |
url | string | 必填 | Tautulli 服务地址(含端口) |
key | string | 必填 | Tautulli API Key |
enableUser | boolean | false | 是否在流标题后显示播放用户(friendly_name) |
showEpisodeNumber | boolean | false | 对剧集类媒体是否显示 Sxx · Exx 集数信息 |
expandOneStreamToTwoRows | boolean | true | 当只有一个活跃播放流时,是否展开为两行显示(标题行 + 进度行) |
这三个布尔参数的默认值均有对应源码实现支撑,详见下文第四、五节。
四、源码级原理:请求如何从 Widget 到达 Tautulli
4.1 API 模板与 Endpoint 映射
Tautulli Widget 的核心定义位于 src/widgets/tautulli/widget.js,代码非常精简:
import genericProxyHandler from "utils/proxy/handlers/generic"; const widget = { api: "{url}/api/v2?apikey={key}&cmd={endpoint}", proxyHandler: genericProxyHandler, mappings: { get_activity: { endpoint: "get_activity", }, }, }; export default widget;可以提炼出几个关键事实:
- Widget 通过 Tautulli 的API v2接口获取数据,最终请求 URL 形如
{url}/api/v2?apikey={key}&cmd=get_activity; cmd=get_activity是 Tautulli API 中用于查询当前活跃会话的命令,与本文档"active streams"的定位完全对应;- 该 Widget 使用通用的
genericProxyHandler作为代理处理器,没有自定义的响应映射(mappings仅声明 endpoint)。
4.2 通用代理链路:服务端如何透传请求
Homepage 的所有 Widget 请求都先打到项目自身的 API 路由,再由服务端代理转发到目标服务,避免在浏览器端暴露 API Key。Tautulli Widget 走的是通用链路,核心实现在 src/utils/proxy/handlers/generic.js:
- 从请求 query 中解析
group、service、endpoint、index; - 通过
getServiceWidget读取当前服务的 Widget 配置; - 使用
formatApiCall(widgets[widget.type].api, { endpoint, ...widget })将{url}、{key}、{endpoint}等占位符替换为实际值,拼出最终 URL; - 通过
httpProxy发起 HTTP 请求,并做响应校验(validateWidgetData)与错误信息脱敏(sanitizeErrorURL)。
前端侧,组件通过 src/utils/proxy/use-widget-api.js 中封装的useSWR拉取数据,并为get_activity设置了5000ms(5 秒)的刷新间隔(见 src/widgets/tautulli/component.jsx):
const { data: activityData, error: activityError } = useWidgetAPI(widget, "get_activity", { refreshInterval: 5000, });也就是说,只要页面处于打开状态,Tautulli Widget 每 5 秒会自动重新请求一次活跃会话数据,保证仪表板上的播放状态接近实时。
五、渲染行为详解:三种展示形态与判断逻辑
Tautulli Widget 的渲染组件在 src/widgets/tautulli/component.jsx 中实现,其展示逻辑可以根据活跃流数量分为三种形态。
5.1 加载态与空态
- 加载中:数据尚未返回时,渲染 1~2 行占位符(
-); - 无活跃流:
sessions为空数组时,显示国际化文案tautulli.no_active(英文为 "No Active Streams",简体中文为"暂无播放",见 public/locales/en/common.json 与 public/locales/zh-Hans/common.json); - 连接错误:请求失败或返回空数据时,显示
tautulli.plex_connection_error(英文 "Check Plex Connection",简体中文"检查Plex连接"),提示检查 Plex/Tautulli 连通性。
5.2 单流双行展开(expandOneStreamToTwoRows)
当expandOneStreamToTwoRows为true(默认值)且当前恰好只有一个活跃播放流时,Widget 使用SingleSessionEntry渲染两行:
- 第一行(标题行):展示流标题,并在右侧显示播放决策图标;
- 第二行(进度行):带进度的进度条背景,左侧显示播放/暂停图标,右侧显示
当前观看位置 / 总时长(格式为HH:MM:SS或MM:SS)。
多流时(2 个及以上),则退化为紧凑的单行模式SessionEntry:每行显示状态图标、标题、播放决策图标和当前观看位置,不再展示完整进度条与总时长。
这个开关的默认值在源码中是这样生效的:
const expandOneStreamToTwoRows = service.widget?.expandOneStreamToTwoRows !== false; // default is true即只有当显式配置为false时才会关闭双行展开,不配置即为true。
5.3 标题与集数显示(enableUser / showEpisodeNumber)
标题的生成逻辑由generateStreamTitle完成:
function generateStreamTitle(session, enableUser, showEpisodeNumber) { let stream_title = ""; const { media_type, parent_media_index, media_index, title, grandparent_title, full_title, friendly_name } = session; if (media_type === "episode" && showEpisodeNumber) { const season_str = `S${parent_media_index.toString().padStart(2, "0")}`; const episode_str = `E${media_index.toString().padStart(2, "0")}`; stream_title = `${grandparent_title}: ${season_str} · ${episode_str} - ${title}`; } else { stream_title = full_title; } return enableUser ? `${stream_title} (${friendly_name})` : stream_title; }showEpisodeNumber: true:当媒体类型为剧集(episode)时,标题显示为剧名: S01 · E05 - 单集标题的格式,方便快速定位季/集;enableUser: true:在标题末尾追加(用户名),其中用户名取自 Tautulli 会话中的friendly_name字段,便于在多用户家庭环境中区分谁在观看。
5.4 播放决策图标:Direct Play / Copy / Transcode
每行右侧的图标用于表达 Tautulli 返回的video_decision与audio_decision组合:
| 视频/音频决策 | 图标 | 含义 |
|---|---|---|
均为direct play | MdSmartDisplay(实心显示器) | 直接播放,无任何转码 |
均为copy | MdOutlineSmartDisplay(描边显示器) | 容器/流复制(remux),接近无损 |
| 其他组合 | BsFillCpuFill/BsCpu | 涉及转码,CPU 参与处理 |
图标语义结合国际化文案tautulli.playing(播放中)、tautulli.transcoding(转码)、tautulli.bitrate(比特率)使用,可直观判断当前播放流的资源占用情况。
5.5 排序规则
获取到会话列表后,组件会对sessions按view_offset(当前观看位置,毫秒)进行升序排序后再渲染,即观看进度靠前的播放流显示在上面:
const playing = activityData.response.data.sessions.sort((a, b) => { if (a.view_offset > b.view_offset) return 1; if (a.view_offset < b.view_offset) return -1; return 0; });六、测试与验证:Widget 行为的自动化保障
仓库为 Tautulli Widget 提供了完整的测试,可作为理解其行为与验证配置合法性的参考:
- src/widgets/tautulli/widget.test.js:通过
expectWidgetConfigShape校验 Widget 配置对象(api、proxyHandler、mappings等)符合项目约定的结构,任何字段缺失都会导致测试失败; - src/widgets/tautulli/component.test.jsx:使用
vi.mock模拟useWidgetAPI,覆盖三类关键场景:- 加载中显示占位行(
-); - 无活跃会话时显示
tautulli.no_active文案; - 单个会话播放时渲染双行展开,且时间格式正确(如
view_offset: 1000毫秒渲染为00:01,duration: 2000渲染为00:02)。
- 加载中显示占位行(
这些测试一方面验证了渲染逻辑的正确性,另一方面也侧面印证了本文第五节描述的展示规则。
七、常见问题与排错
1. Widget 显示"检查Plex连接"(Check Plex Connection)
该错误对应tautulli.plex_connection_error,出现时说明请求 Tautulli 失败或返回数据为空。请依次检查:
url是否可从运行 Homepage 的主机访问(注意不要写成 Plex 的地址,而是 Tautulli 的地址);key是否复制完整,API Key 在Settings > Web Interface > API中查看;- Tautulli 服务是否运行正常、端口是否正确。
2. 显示"暂无播放"(No Active Streams)但 Plex 明明在播放
确认 Plex 用户确实在当前活跃播放(而非暂停很久或已退出),Tautulli 的get_activity接口只返回当前处于活动状态的会话。另外注意 Widget 每 5 秒刷新一次,数据可能存在数秒延迟。
3. 不想看到用户名 / 不想显示集数
分别将enableUser、showEpisodeNumber配置为false(或不配置,因为默认即false)。
4. 希望单流时也保持一行
将expandOneStreamToTwoRows显式设置为false,此时无论活跃流数量多少,都使用紧凑单行渲染。
八、小结
Homepage 的 Tautulli Widget 以"实时活跃播放流监控"为单一职责,配置只需url+key两个必填项,配合enableUser、showEpisodeNumber、expandOneStreamToTwoRows三个可选开关即可适配不同展示偏好。底层通过服务端通用代理调用 Tautulli API v2 的get_activity命令,前端每 5 秒刷新,并针对 Direct Play / Copy / Transcode 提供直观的图标反馈。若需深入理解实现细节,可继续阅读 src/widgets/tautulli/widget.js、src/widgets/tautulli/component.jsx 以及对应的 组件测试。
【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考