Skip to content

Live IPTV

bestZwei edited this page Sep 10, 2026 · 3 revisions

直播 / IPTV

LibreTV 的直播模块:解析 M3U 订阅,在 /live 页面按分组浏览、搜索频道并站内播放,支持 HLS(m3u8)与 HTTP-FLV 两种直播流,可选 XMLTV 节目单(EPG)。

沿承项目的空壳设计:不内置任何频道源,也不存储、不制作任何直播内容,仅提供第三方公开播放列表的解析与播放能力,内容合法性由数据源负责。

入口与整体结构

  • 顶部导航「直播」→ /live
  • 页面布局:左侧播放器 + 频道信息条 + 节目单;右侧频道侧栏(视图 tab / 分组标签条 / 搜索 / 频道列表);
  • 状态由 URL 驱动:/live?url=<流地址>&name=<频道名>&group=<分组>&tvgId=<EPG id>&epg=<EPG 地址>,刷新 / 分享深链无损。

添加直播源

直播源有三个来源:

  1. 手动添加:设置 → 直播源 → 填入 M3U 地址 → 「添加直播源」;
  2. 订阅导入:设置 → 订阅与配置 → 数据源订阅,订阅 JSON 里的 liveSources 会与点播源一起导入,见 Data-Sources
  3. 部署者预置DEFAULT_LIVE_SOURCES 环境变量,行为对齐点播源的 DEFAULT_SOURCES(首次自动启用、用户可停用、不可删除),见 Configuration

手动添加的字段:

字段 必填 说明
M3U 地址 形如 https://example.com/list.m3u,服务端拉取并解析
名称 显示用名称,缺省时用地址主机名
EPG 节目单地址 XMLTV 格式(xml / xml.gz),用于节目单展示
  • 添加后自动探活:拉取解析并显示频道数量(✓ 143 频道 / ✗ 失败);
  • 每个源前的 勾选框 控制启用/停用:停用后 /live 页不再聚合该源的频道(无需删除);
  • 导出:把该源解析后的内容导出为标准 M3U 文件,供 PotPlayer / VLC / TiMiMate 等外部播放器使用;
  • 来自订阅的源带 「订阅」 标识:以远端列表为准,不可单独删除(点击会提示去订阅区操作),同步时整体替换其名称/地址/EPG;只有删除整个订阅才会移除该源,且已收藏的频道会保留

公开测试源

iptv-org(全球最大公开 IPTV 索引,社区维护):

中国频道   https://iptv-org.github.io/iptv/countries/cn.m3u
中文频道   https://iptv-org.github.io/iptv/languages/zho.m3u
新闻分类   https://iptv-org.github.io/iptv/categories/news.m3u
全球全量   https://iptv-org.github.io/iptv/index.m3u      (1 万+ 频道,慎用)

EPG 可搭配 https://epg.pw/xmltv/epg_CN.xml(注意:不同源的 tvg-id 命名规则不一致,EPG 匹配率取决于列表质量)。

频道侧栏

区域 说明
视图 tab 全部 / 收藏(星标)/ 最近(自动记录,上限 20 条)
分组标签条 按 M3U 的 group-title 横向滑动筛选
搜索 匹配频道名与 tvg-id
批量测活 ⚡ 测活:分片级探测当前筛选范围的频道(见下文),频道名前显示状态点;结果 6 小时内有效,重复点击只补测失效/未测的频道;可点「仅可用 N」只看可用频道

大列表渐进渲染(每次 300 条),数千频道的全量列表不会卡顿。

播放

  • 协议分发:.m3u8 → hls.js(直播参数:追帧小缓冲);.flv → mpegts.js(动态加载,不占点播首屏体积);
  • 代理回退:直连失败(CORS / 防盗链)时自动改走 /api/live/stream/ 重试一次;
  • 直播态 UI:无进度条 / 倍速 / 截图 / 自动连播;右下角 LIVE 呼吸标识;
  • H.265 检测:manifest 含 hvc1/hev1 且浏览器不支持时提示「建议用 Edge/Safari」(国内运营商源大量使用 H.265,Chromium 内核通常无法软解);
  • 错误提示带 HTTP 状态码(如 直播流加载失败(HTTP 403)),便于定位是 token 校验失败还是源瞬断;
  • 断流保护:起播后连续 5 次致命网络错误即停止重试并报错,避免无限循环。

与点播播放器的关系

直播播放器(live-player.tsx)与点播播放器(player-shell.tsx完全独立:直播没有进度恢复 / 历史记录 / 自动连播 / 广告过滤(#EXT-X-DISCONTINUITY 过滤对直播有误伤风险),参数也是直播调优值。修改点播行为不会影响直播,反之亦然。

批量测活(分片级)

侧栏「⚡ 测活」触发 POST /api/live/probe

探测方式

  • 单级探测(仅 manifest 可访问)不可信:大量源 manifest 正常但分片请求被拒(token/Referer 校验、源瞬断)。因此探测追到真实分片: m3u8 →(master 则穿透第一个 variant)→ media playlist → 第一个分片,分片以 Range: bytes=0-1 轻量请求,拿到响应头即断开;
  • 请求携带源站 Referer,避免源校验 Referer 时把可播的频道误判为不可用;
  • 校验 #EXTM3U 前缀与直链 content-type:返回 200 的 HTML/JSON 错误页不会被判为可用;
  • LIVE_ALLOW_PRIVATE=1 时自建内网 IPTV 同样能测通(与播放代理同一套 SSRF 口径)。

结果含义

  • 状态点:🟢 绿 = 可达(最可信);🔴 红 = 不可达;无点 = 未探测;
  • 悬浮 tooltip 显示探测深度与耗时:分片可用 · 分片耗时 320ms / 直链可达 / manifest 可用(空播放列表降级);
  • H.265 徽章:master playlist 声明 hvc1/hev1 编码时,频道名旁显示琥珀色 H.265 标记——这类频道测活会通过,但 Chromium 内核通常无法解码,需用 Edge / Safari。

性能与缓存

  • 服务端:每个频道 6s 整体时间预算(分片级上限 2.5s),并发 16;结果带短 TTL 缓存(成功 10 分钟 / 失败 2 分钟),多用户重复测活不重复请求上游;
  • 流式返回:?stream=1 以 NDJSON 逐条推送,命中缓存的立即回包,前端状态点边测边亮;不带该参数仍返回整批 JSON;
  • 客户端:每批 50 条、2 批并行(单轮上限 400 条),结果 200ms 节流合并写回;重新测活会中断在途请求;
  • 浏览器端结果持久化 6 小时(localStorage,随 libretv-settings):刷新页面、重进直播页都直接显示上次状态;点「清除结果」可手动清空。

局限

  • 结果为 best-effort:网络可达 ≠ 一定能播,H.265 编码、Geo-block、播放器解码能力只能在播放阶段暴露(H.265 已尽量提前用徽章提示);
  • 公共源波动大,超过 6 小时或源异常时重新测活即可。

节目单(EPG)

  • 来源:XMLTV 格式(支持 gzip),在订阅上配置 EPG 地址;
  • 展示:当前节目(含已播进度条、简介展开)+「接下来」列表;
  • 匹配:M3U 频道的 tvg-id ↔ XMLTV <programme channel="...">;匹配率取决于列表质量,未匹配时优雅降级为提示文案;
  • 解析:服务端单趟扫描、按 24h 时间窗裁剪后索引,gzip 自动解压;结果缓存 6 小时(独立大对象缓存,不与豆瓣等热点数据争抢);客户端按需查询当前频道的 tvg-id,不做全量下发。

订阅导出

直播源条目上的 按钮请求 /api/live/playlist?url=<订阅>&format=m3u:把解析后的频道(含 tvg-id / logo / 分组)重新序列化为标准 M3U 文本下载。适合把散乱的订阅清洗成结构规整的列表供外部播放器使用。

播放不了?排查顺序

  1. 测活:侧栏对当前分组「⚡ 测活」,红色频道直接跳过;
  2. 看状态码:播放失败提示中的 HTTP 403 多为 token/防盗链,HTTP 404 多为源瞬断,超时多为源已死;
  3. 看 H.265 徽章 / 换浏览器:黑屏但测活绿色,多半是 HEVC 编码(列表会显示 H.265 徽章)——用 Edge / Safari 或支持 HEVC 的环境打开;
  4. 代理回退:出现「直连失败,改用代理重试」说明源有 CORS/防盗链,代理通常可解;代理也失败时注意该源可能限制服务器 IP 访问;
  5. 确认编码与地区[Geo-blocked] 频道跨地区无法播放;[Not 24/7] 频道仅在部分时段有信号。

技术实现要点(开发者)

  • 直播流代理 /api/live/stream/[url] 与点播 /api/proxy 分离:仅对响应头设 15s 超时,body 无限时长流式透传(HTTP-FLV 是单一长连接,点播代理的全局 8s 超时会切断它);不重试;Cache-Control: no-store, no-transform;客户端断开即 abort 上游;
  • m3u8 重写以重定向后的最终 URL 为 base(gslb 调度源 302 后路径会变,用入口 URL 解析相对分片地址会 404);
  • 每一跳强制 SSRF 校验;LIVE_ALLOW_PRIVATE=1 可显式放行内网自建源(默认关闭),见 Proxy-Security
  • 测活复用同一 SSRF 口径:fetch 内部逐跳校验,测活不再重复预检;解析出的相对地址一律以重定向后的最终 URL 为 base;
  • 状态存储:直播源(含 fromSubscription 归属字段)、启用状态、收藏、最近观看(含 sourceUrl)、测活结果(6 小时 TTL)均在 zustand persist(localStorage libretv-settings);频道列表不落库,每次进入页面从服务端缓存拉取;
  • SSRF:M3U 列表拉取、测活与流代理统一走直播策略 checkLiveUrlAllowedLIVE_ALLOW_PRIVATE=1拉列表、测活与拉流三个阶段同时生效,见 Proxy-Security

Clone this wiki locally