视频:修 media.php 的 304/Range 冲突,HEVC 解不了时给明确提示 (#52)

304 判定没有排除 Range 请求:视频第二次打开时浏览器同时发
Range + If-None-Match,命中 304 拿到空响应 ⇒ 黑屏。
改成 $hasRange 为真时跳过 304。顺手补 Last-Modified(部分浏览器需要它才允许 seek)。

另查明用户的视频是 H.265/HEVC,Chrome/Firefox 无法解码 —— 浏览器端无解,
检测到解码失败时显示提示卡片 + 下载入口,不再给一块黑屏。
This commit is contained in:
2026-11-05 12:40:52 +08:00
parent f92a98a3a3
commit a1140437a1
3 changed files with 225 additions and 4 deletions
+21 -1
View File
@@ -217,8 +217,25 @@ $etag = '"' . md5($key . '|' . $size . '|' . $mime) . '"';
v47 的缓存回写(见第 3 步)让第二次开始命中的是**本地文件**,
所以这里"取回来再判 304"的代价已经降到本地读盘级别,够用了。
(真要连本地读都省掉,得把 ETag 单独持久化 —— 那是下一步的事。) */
/* 🔴🔴 2026-11-05 修(用户:「网页端的视频…点开之后是黑的,没办法播放」):
**带 Range 的请求绝不能回 304**。
原来的顺序是"先判 304,再处理 Range"。视频第一次播完后浏览器缓存了 ETag,
拖动进度条 / 第二次打开时会同时发:
Range: bytes=1048576-
If-None-Match: "abc123"
⇒ 命中 304(304 **没有响应体**)⇒ `<video>` 拿不到数据 ⇒ **黑屏**。
这就是"有时候能播、有时候黑"的根因:取决于浏览器有没有带 ETag。
RFC 7233 也明确:304 不能替代对 Range 的响应。
⇒ 有 Range 就跳过 304,直接走第 6 步。 */
$hasRange = trim((string)($_SERVER['HTTP_RANGE'] ?? '')) !== '';
$inm = trim((string)($_SERVER['HTTP_IF_NONE_MATCH'] ?? ''));
if ($inm !== '' && $inm === $etag) {
if (!$hasRange && $inm !== '' && $inm === $etag) {
http_response_code(304);
header('ETag: ' . $etag);
header('Cache-Control: private, max-age=604800');
@@ -261,6 +278,9 @@ if ($isRange) {
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
}
header('Accept-Ranges: bytes');
/* 2026-11-05:补 Last-Modified —— 部分浏览器/Safari 需要它才允许 seek,
缺了会对某些 mp4 直接拒绝播放(表现为黑屏 + 无报错)。 */
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
$filename = basename($key);
$disp = !empty($_GET['dl']) ? 'attachment' : 'inline';
header('Content-Disposition: ' . $disp . '; filename="' . rawurlencode($filename) . '"');