mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 13:26:38 +08:00
优化起播
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/truewhile/MeBox/internal/model"
|
||||
"github.com/truewhile/MeBox/internal/repository"
|
||||
@@ -33,6 +34,14 @@ func (s *StreamService) ServeFileWithCloudMode(w http.ResponseWriter, r *http.Re
|
||||
if !cloudPlaybackModeEnabled(r.Context(), s.repo, cloudMode) {
|
||||
return ErrCloudPlaybackDisabled
|
||||
}
|
||||
// 能在服务端换到最终直链就直接 302 过去:客户端少跟随一次 302,等于
|
||||
// 省掉一次「DNS+TCP+TLS+请求」的往返。解析结果同时写进 strm 层直链
|
||||
// 缓存,后续 /api/strm/play 请求直接命中。
|
||||
if direct, ok := s.resolveDirectPlayTargetURL(r, strmURL); ok {
|
||||
setCloudRedirectNoStore(w)
|
||||
http.Redirect(w, r, direct, http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
// 云盘播放 URL 先规范化为相对路径,免疫扫描时固化的旧 host;
|
||||
// 指向别的 MeBox 实例的地址保持原样,按第三方直链透传。
|
||||
target := normalizeCloudPlayTarget(r.Context(), s.repo, s.cfg, r, strmURL)
|
||||
@@ -72,6 +81,43 @@ func setCloudRedirectNoStore(w http.ResponseWriter) {
|
||||
w.Header().Set("Expires", "0")
|
||||
}
|
||||
|
||||
// directPlayResolveTimeout 是服务端换链的等待上限。115 开放平台在跨太平洋线路
|
||||
// 上单次换链实测 0.4–1.1s,这里给足余量;一旦超时就回退到原来的 strm 端点跳转,
|
||||
// 由 /api/strm/play 再去换链,最坏情况只是回到改动前的行为。
|
||||
const directPlayResolveTimeout = 10 * time.Second
|
||||
|
||||
// resolveDirectPlayTargetURL 尝试在服务端把 strm 目标解析成客户端可直接拉取的
|
||||
// 最终直链,供调用方直接 302。
|
||||
//
|
||||
// 只在「明确的直链」上短路:需要服务端反向代理(云盘 WebDAV 等必须附加请求头)、
|
||||
// 解析到本地文件、以及解析失败都会返回 false,由调用方按改动前的方式回退到
|
||||
// strm 端点跳转,行为不会变差。
|
||||
func (s *StreamService) resolveDirectPlayTargetURL(r *http.Request, raw string) (string, bool) {
|
||||
if s == nil || s.strmResolve == nil || r == nil {
|
||||
return "", false
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), directPlayResolveTimeout)
|
||||
defer cancel()
|
||||
// 必须带播放器 UA:115 直链绑定换取时的 UA,且按 UA 分键缓存,换错会拿到
|
||||
// 与播放器不匹配(或未命中缓存)的地址。
|
||||
result, err := s.strmResolve(ctx, raw, r.Header.Get("User-Agent"))
|
||||
if err != nil || result == nil || result.Proxy || result.Link == nil {
|
||||
return "", false
|
||||
}
|
||||
// 客户端只能自带 User-Agent 这类基础请求头。链接一旦要求其它头(Referer /
|
||||
// Authorization),就必须继续由服务端反向代理,不能在这里短路。
|
||||
for name := range result.Link.Headers {
|
||||
if !strings.EqualFold(strings.TrimSpace(name), "User-Agent") {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
direct := strings.TrimSpace(result.RedirectURL)
|
||||
if direct == "" {
|
||||
return "", false
|
||||
}
|
||||
return direct, true
|
||||
}
|
||||
|
||||
func isCloudPlaybackTarget(raw string) bool {
|
||||
_, _, ok := parseCloudMediaPlaybackURL(raw)
|
||||
return ok
|
||||
|
||||
Reference in New Issue
Block a user