fix(preview): Twitch 不能直连的原因改为实测结论——清单服务器按 Origin 白名单放行 (#1715)
HLS 直连补测各平台清单 / 分片的跨域头:B 站 fMP4 的 m3u8 / init / .m4s 同域 ACAO *;抖音 TS 的主清单与 媒体清单 + 分片不同域(douyinliving.com → 100ycdn.com)都 *;虎牙 TS 同域、回显 Origin(清单轮询有 边缘节点间歇 403,靠重取 / 回落)。Twitch:usher 主清单无 ACAO,媒体清单 / 分片带 ACAO *,但清单 服务器对 Origin 做白名单——twitch.tv 与 localhost 200、127.0.0.1 / 其它站点 403,所以部署到自己域名 后浏览器直连必 403,仍标不能直连,原因改成这句。弹幕 SSE 两个测试改用各自的信号量,不再与并行 测试抢全局那把(偶发失败)。 Part of #1708
This commit is contained in:
2 files changed
+22
-29
No files matched your search
@@ -280,8 +280,8 @@ const Global: React.FC = () => {
|
||||
<div>
|
||||
<strong>浏览器直连 CDN</strong>:biliup 向平台另取一条直链(新 token,不影响录制那条),浏览器自己去
|
||||
CDN 拉,媒体流量不经过 biliup,适合 biliup 部署在异地服务器、浏览器远程访问的情况,省服务器出口带宽。
|
||||
只有 CDN 放行跨域的平台能直连(B 站 / 抖音 / 斗鱼 / 虎牙,FLV 与 HLS 都行);Twitch 等未放行跨域的
|
||||
平台在直连模式下自动回落中转并在播放器角标标出原因。
|
||||
只有 CDN 放行跨域的平台能直连(B 站 / 抖音 / 斗鱼 / 虎牙,FLV 与 HLS 都行);Twitch 等按 Origin 白名单放行
|
||||
的平台在直连模式下自动回落中转并在播放器角标标出原因。
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -95,12 +95,14 @@ fn subscribe_error_response(id: i64, error: SubscribeError) -> Response {
|
||||
/// 直连**不复用录制那条直链**而是向平台另取一条(新 token),所以「同一直链两条连接」的限制
|
||||
/// (斗鱼一 token 一连接、部分虎牙节点对第二条连接只给 GOP 缓存即断)不再是问题——实测新 token
|
||||
/// 的斗鱼 / 虎牙直链各读 25 s 稳定,录制那条不受影响。容器不影响判定:FLV 走 mpegts.js,
|
||||
/// HLS(TS / fMP4 分片,`.m3u8`)走 hls.js,B 站 `.m4s` 分片的 CDN 同样 `ACAO: *`。
|
||||
/// HLS(TS / fMP4 分片,`.m3u8`)走 hls.js。HLS 实测:B 站 fMP4 的 m3u8 / init / `.m4s` 同域同 `ACAO: *`;
|
||||
/// 抖音 TS 的主清单(`pull-hls-*.douyinliving.com`)与媒体清单 + 分片(`*.100ycdn.com`)不同域,都 `*`;
|
||||
/// 虎牙 TS 的 m3u8 与分片同域,回显 Origin(清单轮询有边缘节点间歇 403,靠重取 / 回落)。
|
||||
///
|
||||
/// | 平台 | ACAO | 判定 |
|
||||
/// | 平台 | 跨域 | 判定 |
|
||||
/// | --- | --- | --- |
|
||||
/// | B 站 / 抖音 / 虎牙 / 斗鱼 | `*` | 能 |
|
||||
/// | Twitch | 200 响应无 ACAO | 不能 |
|
||||
/// | B 站 / 抖音 / 虎牙 / 斗鱼 | FLV 与 HLS 的清单、分片都放行 | 能 |
|
||||
/// | Twitch | usher 主清单无 ACAO;媒体清单 / 分片虽带 `ACAO: *`,但清单服务器按 Origin 白名单放行(只有 twitch.tv 与 localhost),其它站点 403 | 不能 |
|
||||
///
|
||||
/// 其它平台没实测,按不能处理,回落中转。
|
||||
pub fn direct_capability(platform: &str) -> DirectCapability {
|
||||
@@ -113,7 +115,9 @@ pub fn direct_capability(platform: &str) -> DirectCapability {
|
||||
capable: true,
|
||||
reason: None,
|
||||
},
|
||||
"twitch" => no("Twitch CDN 未放行跨域(响应无 Access-Control-Allow-Origin)"),
|
||||
"twitch" => no(
|
||||
"Twitch 的清单服务器按 Origin 白名单放行(只有 twitch.tv 与 localhost),其它站点跨域请求 403",
|
||||
),
|
||||
_ => no("该平台的 CDN 跨域放行未验证"),
|
||||
}
|
||||
}
|
||||
@@ -585,12 +589,7 @@ mod tests {
|
||||
}
|
||||
let twitch = direct_capability("twitch");
|
||||
assert!(!twitch.capable);
|
||||
assert!(
|
||||
twitch
|
||||
.reason
|
||||
.unwrap()
|
||||
.contains("Access-Control-Allow-Origin")
|
||||
);
|
||||
assert!(twitch.reason.unwrap().contains("Origin"));
|
||||
assert!(!direct_capability("youtube").capable);
|
||||
// 容器由直链后缀决定播放器:flv → mpegts.js,m3u8 → hls.js
|
||||
assert_eq!(
|
||||
@@ -639,10 +638,9 @@ mod tests {
|
||||
use danmaku_client::message::EnterMessage;
|
||||
use danmaku_client::{ChatMessage, GiftMessage};
|
||||
let (tx, rx) = tokio::sync::broadcast::channel(8);
|
||||
let permit = danmaku_connection_limit()
|
||||
.clone()
|
||||
.try_acquire_owned()
|
||||
.unwrap();
|
||||
// 各测试用自己的信号量,别和并行跑的其它测试抢全局那把
|
||||
let limit = Arc::new(Semaphore::new(1));
|
||||
let permit = limit.clone().try_acquire_owned().unwrap();
|
||||
let response = danmaku_response(vec![(7, rx)], permit);
|
||||
assert_eq!(
|
||||
response.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
@@ -688,10 +686,8 @@ mod tests {
|
||||
"{text}"
|
||||
);
|
||||
assert!(!text.contains("路人"), "{text}");
|
||||
assert_eq!(
|
||||
danmaku_connection_limit().available_permits(),
|
||||
MAX_DANMAKU_CONNECTIONS
|
||||
);
|
||||
// 响应体读完即 drop,许可随之释放
|
||||
assert_eq!(limit.available_permits(), 1);
|
||||
}
|
||||
|
||||
/// 复用:两路广播合成一条 SSE,每条事件带各自的 id;两路发送端都 drop 后流才结束。
|
||||
@@ -700,10 +696,9 @@ mod tests {
|
||||
use danmaku_client::ChatMessage;
|
||||
let (tx_a, rx_a) = tokio::sync::broadcast::channel(8);
|
||||
let (tx_b, rx_b) = tokio::sync::broadcast::channel(8);
|
||||
let permit = danmaku_connection_limit()
|
||||
.clone()
|
||||
.try_acquire_owned()
|
||||
.unwrap();
|
||||
// 各测试用自己的信号量,别和并行跑的其它测试抢全局那把
|
||||
let limit = Arc::new(Semaphore::new(1));
|
||||
let permit = limit.clone().try_acquire_owned().unwrap();
|
||||
let response = danmaku_response(vec![(1, rx_a), (3, rx_b)], permit);
|
||||
tx_a.send(DanmakuEvent::Chat(ChatMessage::new("来自1".into())))
|
||||
.unwrap();
|
||||
@@ -727,10 +722,8 @@ mod tests {
|
||||
"{text}"
|
||||
);
|
||||
assert!(text.contains("b还在"), "{text}");
|
||||
assert_eq!(
|
||||
danmaku_connection_limit().available_permits(),
|
||||
MAX_DANMAKU_CONNECTIONS
|
||||
);
|
||||
// 响应体读完即 drop,许可随之释放
|
||||
assert_eq!(limit.available_permits(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user