mirror of
https://github.com/krau/SaveAny-Bot.git
synced 2026-06-25 17:23:50 +08:00
feat: add configuration options for video download settings
This commit is contained in:
@@ -33,6 +33,17 @@ secret = ""
|
||||
# 转存完成后删除 Aria2 下载的本地文件
|
||||
remove_after_transfer = true
|
||||
|
||||
# yt-dlp 视频下载配置
|
||||
[ytdlp]
|
||||
# 默认下载的最高视频清晰度 (按高度限制), 如 1080, 720, 480; 0 表示不限制 (下载最佳画质)
|
||||
# 仅在使用 /ytdlp 命令且未手动指定任何参数时生效
|
||||
max_height = 1080
|
||||
# 直接指定 yt-dlp format 选择表达式, 留空则使用 max_height
|
||||
# 设置后优先级高于 max_height, 例如: "bv*[height<=720]+ba/b"
|
||||
format = ""
|
||||
# 下载后转封装的视频容器格式, 留空则不转封装. 默认 mp4
|
||||
recode = "mp4"
|
||||
|
||||
# HTTP API 配置
|
||||
[api]
|
||||
# 启用 HTTP API
|
||||
|
||||
@@ -35,6 +35,7 @@ type Config struct {
|
||||
Storages []storage.StorageConfig `toml:"-" mapstructure:"-" json:"storages"`
|
||||
Parser parserConfig `toml:"parser" mapstructure:"parser" json:"parser"`
|
||||
Hook hookConfig `toml:"hook" mapstructure:"hook" json:"hook"`
|
||||
Ytdlp YtdlpConfig `toml:"ytdlp" mapstructure:"ytdlp" json:"ytdlp"`
|
||||
}
|
||||
|
||||
type aria2Config struct {
|
||||
@@ -131,6 +132,9 @@ func Init(ctx context.Context, configFile ...string) error {
|
||||
"api.host": "0.0.0.0",
|
||||
"api.port": 8080,
|
||||
"api.token": "",
|
||||
|
||||
// yt-dlp
|
||||
"ytdlp.recode": "mp4",
|
||||
}
|
||||
|
||||
for key, value := range defaultConfigs {
|
||||
|
||||
13
config/ytdlp.go
Normal file
13
config/ytdlp.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
type YtdlpConfig struct {
|
||||
// MaxHeight limits the video resolution by height in pixels (e.g. 1080, 720).
|
||||
// 0 means no limit (best available). Ignored when Format is set.
|
||||
MaxHeight int `toml:"max_height" mapstructure:"max_height" json:"max_height"`
|
||||
// Format is a raw yt-dlp format selector (-f). When set, it takes precedence
|
||||
// over MaxHeight and gives the user full control.
|
||||
Format string `toml:"format" mapstructure:"format" json:"format"`
|
||||
// Recode is the target video container yt-dlp recodes into (e.g. mp4).
|
||||
// Empty disables recoding.
|
||||
Recode string `toml:"recode" mapstructure:"recode" json:"recode"`
|
||||
}
|
||||
@@ -85,12 +85,10 @@ func (t *Task) downloadFiles(ctx context.Context, tempDir string) ([]string, err
|
||||
cmd := ytdlp.New().
|
||||
Output(filepath.Join(tempDir, "%(title)s.%(ext)s"))
|
||||
|
||||
// If no custom flags are provided, use default behavior
|
||||
// Apply config-based format/quality defaults only when the user passes no
|
||||
// custom flags. Any user flag means they take full control of yt-dlp.
|
||||
if len(t.Flags) == 0 {
|
||||
cmd = cmd.
|
||||
FormatSort("res,ext:mp4:m4a").
|
||||
RecodeVideo("mp4").
|
||||
RestrictFilenames()
|
||||
cmd = applyFormatConfig(cmd, config.C().Ytdlp)
|
||||
}
|
||||
// Note: If custom flags are provided, users have full control over format/quality
|
||||
// The output path is always set above to ensure downloads go to the correct directory
|
||||
|
||||
40
core/tasks/ytdlp/format.go
Normal file
40
core/tasks/ytdlp/format.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package ytdlp
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
ytdlp "github.com/lrstanley/go-ytdlp"
|
||||
|
||||
"github.com/krau/SaveAny-Bot/config"
|
||||
)
|
||||
|
||||
// buildFormatSelector translates a max height into a yt-dlp format selector.
|
||||
// It prefers merging the best video+audio within the height limit, then falls
|
||||
// back to a single muxed stream. An empty result means "no explicit selector".
|
||||
func buildFormatSelector(maxHeight int) string {
|
||||
if maxHeight <= 0 {
|
||||
return ""
|
||||
}
|
||||
h := strconv.Itoa(maxHeight)
|
||||
return "bv*[height<=" + h + "]+ba/b[height<=" + h + "]/b"
|
||||
}
|
||||
|
||||
// applyFormatConfig configures format/quality on the yt-dlp command according to
|
||||
// the ytdlp config. It is only meant to be called when the user did not supply
|
||||
// any custom flags, so config-driven defaults never conflict with user input.
|
||||
func applyFormatConfig(cmd *ytdlp.Command, cfg config.YtdlpConfig) *ytdlp.Command {
|
||||
switch {
|
||||
case cfg.Format != "":
|
||||
cmd = cmd.Format(cfg.Format)
|
||||
case cfg.MaxHeight > 0:
|
||||
cmd = cmd.Format(buildFormatSelector(cfg.MaxHeight))
|
||||
default:
|
||||
// Preserve the original default: prefer highest resolution mp4/m4a.
|
||||
cmd = cmd.FormatSort("res,ext:mp4:m4a")
|
||||
}
|
||||
if cfg.Recode != "" {
|
||||
cmd = cmd.RecodeVideo(cfg.Recode)
|
||||
}
|
||||
cmd = cmd.RestrictFilenames()
|
||||
return cmd
|
||||
}
|
||||
23
core/tasks/ytdlp/format_test.go
Normal file
23
core/tasks/ytdlp/format_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package ytdlp
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBuildFormatSelector(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
maxHeight int
|
||||
want string
|
||||
}{
|
||||
{"no limit", 0, ""},
|
||||
{"negative", -1, ""},
|
||||
{"1080p", 1080, "bv*[height<=1080]+ba/b[height<=1080]/b"},
|
||||
{"720p", 720, "bv*[height<=720]+ba/b[height<=720]/b"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := buildFormatSelector(tt.maxHeight); got != tt.want {
|
||||
t.Errorf("buildFormatSelector(%d) = %q, want %q", tt.maxHeight, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user