✨ feat(video): 新增视频 CENC 解密与流式代理播放
- 逆向 SpadeA(黑桃A) PlayAuth 混淆,从 spade_a 还原 AES-128 内容密钥
- 实现 cenc-aes-ctr MP4 解密(senc 逐样本 CTR + stsd entry 修复),实测可播放
- 新增 /video/proxy/{key} 代理:稳定 key + 磁盘缓存(二次命中提速 130x)+
http.ServeContent 支持 Range/206/seek 流式播放
- /video 接口新增 output(url/proxy,默认 proxy) 与 quality(max/720p/档位key) 参数,
可由 config fanqie.video.* 配置默认值;无匹配清晰度时明确报错
- 代理映射复用 audio_cache 存储,抽取 loadMediaProxyEntry 共用;挂接 7 天缓存清理
- deploy.sh 新增 --sync-nginx:备份→重写→校验→失败自动回滚,同步视频代理段配置
This commit is contained in:
1 parent
162dde9222
commit
0aed60713a
13 files changed
+1000
-78
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HandleAudio 音频处理
|
||||
@@ -75,7 +76,9 @@ func HandleVideo(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
result, err := fqapi.HandleVideo(params["item_ids"])
|
||||
output := r.URL.Query().Get("output")
|
||||
quality := r.URL.Query().Get("quality")
|
||||
result, err := fqapi.HandleVideo(params["item_ids"], output, quality)
|
||||
if err != nil {
|
||||
respondError(w, "获取视频失败: "+err.Error(), false)
|
||||
return
|
||||
@@ -84,6 +87,30 @@ func HandleVideo(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleVideoProxy 视频解密代理处理(磁盘缓存优先;http.ServeContent 支持 Range/seek/HEAD,
|
||||
// 播放器拖动进度条时按需分片返回)
|
||||
func HandleVideoProxy(w http.ResponseWriter, r *http.Request) {
|
||||
incrementStat("total", "video")
|
||||
|
||||
path := r.URL.Path
|
||||
key := strings.TrimPrefix(path, "/video/proxy/")
|
||||
if key == "" || key == path {
|
||||
http.Error(w, "缺少视频key", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
content, contentType, err := fqapi.HandleVideoProxy(key)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if closer, ok := content.(io.Closer); ok {
|
||||
defer closer.Close()
|
||||
}
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
http.ServeContent(w, r, key+".mp4", time.Time{}, content)
|
||||
}
|
||||
|
||||
// HandleManga 漫画处理
|
||||
func HandleManga(w http.ResponseWriter, r *http.Request) {
|
||||
incrementStat("total", "manga")
|
||||
|
||||
Reference in new issue
Block a user