@@ -5,12 +5,15 @@ import (
55 "net/http"
66 "net/url"
77 "os"
8+ "path/filepath"
89 "strconv"
910 "strings"
1011 "time"
1112
1213 "github.com/OpenListTeam/OpenList/v4/internal/conf"
14+ "github.com/OpenListTeam/OpenList/v4/internal/driver"
1315 "github.com/OpenListTeam/OpenList/v4/internal/fs"
16+ "github.com/OpenListTeam/OpenList/v4/internal/op"
1417 "github.com/OpenListTeam/OpenList/v4/internal/setting"
1518 "github.com/OpenListTeam/OpenList/v4/internal/sign"
1619 "github.com/OpenListTeam/OpenList/v4/internal/transcode"
@@ -65,10 +68,21 @@ func TranscodePlay(c *gin.Context) {
6568 mgr := transcode .Default ()
6669 mgr .Start ()
6770
68- // 构造源签名 URL(让 Worker 通过 /d/ 直接下载)
71+ // 【内存优化】优先尝试解析为本地文件路径,让 ffmpeg 直接读取本地文件,
72+ // 完全绕过 HTTP /d 代理路径——避免 net.Downloader 给每个 range 请求分配
73+ // 高达 MaxBufferLimit (默认系统内存 5%) 的大缓冲。这是降低 Go 进程内存
74+ // 占用最有效的方法。如果不是本地驱动则退回到 HTTP 签名 URL。
6975 apiURL := common .GetApiUrlFromRequest (c .Request )
70- signedPath := sign .Sign (req .Path )
71- sourceURL := fmt .Sprintf ("%s/d%s?sign=%s" , apiURL , encodePath (req .Path ), signedPath )
76+ var sourceURL string
77+ if localFile , ok := resolveLocalFilePath (req .Path ); ok {
78+ // ffmpeg 接受本地路径作为输入,无需 file:// 前缀
79+ sourceURL = localFile
80+ fmt .Printf ("[transcode] using local file path for ffmpeg: %s\n " , localFile )
81+ } else {
82+ // 远程驱动:构造签名 HTTP URL
83+ signedPath := sign .Sign (req .Path )
84+ sourceURL = fmt .Sprintf ("%s/d%s?sign=%s" , apiURL , encodePath (req .Path ), signedPath )
85+ }
7286
7387 // 创建 Job
7488 job := transcode .NewJob ()
@@ -110,6 +124,37 @@ func encodePath(p string) string {
110124 return strings .Join (parts , "/" )
111125}
112126
127+ // resolveLocalFilePath 尝试把 OpenList 路径解析为宿主机文件系统的实际路径。
128+ // 仅对本地驱动 (Local) 有效,其他驱动(云盘等)返回 false。
129+ //
130+ // 这是内存优化的关键路径:本地文件场景下让 ffmpeg 直接读文件,可以完全绕过
131+ // HTTP /d 代理路径上的 net.Downloader 大块缓冲(默认每个 range 请求最高
132+ // 占用 MaxBufferLimit = 系统内存 5%,多 chunk 并发 + 多 range 累计可达数 GB)。
133+ func resolveLocalFilePath (rawPath string ) (string , bool ) {
134+ storage , actualPath , err := op .GetStorageAndActualPath (rawPath )
135+ if err != nil || storage == nil {
136+ return "" , false
137+ }
138+ if storage .Config ().Name != "Local" {
139+ return "" , false
140+ }
141+ rooter , ok := storage .(driver.IRootPath )
142+ if ! ok {
143+ return "" , false
144+ }
145+ root := rooter .GetRootPath ()
146+ if root == "" {
147+ return "" , false
148+ }
149+ full := filepath .Join (root , actualPath )
150+ // 只接受真实存在的文件,避免传给 ffmpeg 一个无效路径
151+ fi , err := os .Stat (full )
152+ if err != nil || fi .IsDir () {
153+ return "" , false
154+ }
155+ return full , true
156+ }
157+
113158// ============================================================
114159// 播放端:/tc/:job/:token/... Master/Variant playlist & 切片
115160// ============================================================
0 commit comments