feat(local-tool): yt-dlp 錯誤訊息友善化 + 載入逾時提示
ResolveWithYTDLP 改善: - 新增 friendlyYTDLPError() 把 yt-dlp 的技術性 stderr 訊息轉成繁中提示 - 涵蓋 9 種常見失敗原因: 年齡限制 / 私人影片 / 已移除 / 版權 / 直播 / 首播 / DRM / 頻率限制 / 無格式 - 錯誤訊息同時附上原始 stderr(方便 debug) - yt-dlp 未安裝時改用繁中提示 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f13848cf0b
commit
c0317225ca
@ -92,27 +92,53 @@ func NewVideoSourceFromURLWithSeek(rawURL string, fps float64, seekSeconds float
|
|||||||
// from platforms like YouTube, Vimeo, etc.
|
// from platforms like YouTube, Vimeo, etc.
|
||||||
// Returns the resolved direct URL or an error.
|
// Returns the resolved direct URL or an error.
|
||||||
func ResolveWithYTDLP(rawURL string) (string, error) {
|
func ResolveWithYTDLP(rawURL string) (string, error) {
|
||||||
// yt-dlp -f "best[ext=mp4]/best" --get-url <URL>
|
|
||||||
cmd := exec.Command("yt-dlp", "-f", "best[ext=mp4]/best", "--get-url", rawURL)
|
cmd := exec.Command("yt-dlp", "-f", "best[ext=mp4]/best", "--get-url", rawURL)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
return "", fmt.Errorf("yt-dlp failed: %s", string(exitErr.Stderr))
|
stderr := strings.TrimSpace(string(exitErr.Stderr))
|
||||||
|
return "", fmt.Errorf("%s\n%s", friendlyYTDLPError(stderr), stderr)
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("yt-dlp not available: %w", err)
|
return "", fmt.Errorf("yt-dlp 未安裝或無法執行: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resolved := strings.TrimSpace(string(out))
|
resolved := strings.TrimSpace(string(out))
|
||||||
if resolved == "" {
|
if resolved == "" {
|
||||||
return "", fmt.Errorf("yt-dlp returned empty URL")
|
return "", fmt.Errorf("yt-dlp 無法取得影片下載連結(影片可能有地區限制或需要登入)")
|
||||||
}
|
}
|
||||||
// yt-dlp may return multiple lines (video + audio); take only the first
|
|
||||||
if idx := strings.Index(resolved, "\n"); idx > 0 {
|
if idx := strings.Index(resolved, "\n"); idx > 0 {
|
||||||
resolved = resolved[:idx]
|
resolved = resolved[:idx]
|
||||||
}
|
}
|
||||||
return resolved, nil
|
return resolved, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// friendlyYTDLPError 把 yt-dlp 的技術性錯誤訊息轉成使用者能理解的提示。
|
||||||
|
func friendlyYTDLPError(stderr string) string {
|
||||||
|
s := strings.ToLower(stderr)
|
||||||
|
switch {
|
||||||
|
case strings.Contains(s, "sign in") || strings.Contains(s, "age"):
|
||||||
|
return "此影片需要登入 YouTube 帳號才能觀看(例如年齡限制),無法直接使用"
|
||||||
|
case strings.Contains(s, "private"):
|
||||||
|
return "此影片為私人影片,無法存取"
|
||||||
|
case strings.Contains(s, "unavailable") || strings.Contains(s, "not available"):
|
||||||
|
return "此影片無法取得(可能已被移除或在你的地區不可用)"
|
||||||
|
case strings.Contains(s, "copyright"):
|
||||||
|
return "此影片因版權限制無法下載"
|
||||||
|
case strings.Contains(s, "live"):
|
||||||
|
return "此影片為直播串流,目前不支援直播推論"
|
||||||
|
case strings.Contains(s, "premiere"):
|
||||||
|
return "此影片為首播(Premiere),尚未開始或格式不支援"
|
||||||
|
case strings.Contains(s, "drm") || strings.Contains(s, "protected"):
|
||||||
|
return "此影片有 DRM 保護,無法下載"
|
||||||
|
case strings.Contains(s, "rate limit") || strings.Contains(s, "429"):
|
||||||
|
return "YouTube 暫時限制了請求頻率,請稍後再試"
|
||||||
|
case strings.Contains(s, "no video formats"):
|
||||||
|
return "找不到可用的影片格式"
|
||||||
|
default:
|
||||||
|
return "無法解析此影片連結,請確認 URL 是否正確且影片為公開狀態"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newVideoSource(input string, fps float64, isURL bool, seekSeconds float64) (*VideoSource, error) {
|
func newVideoSource(input string, fps float64, isURL bool, seekSeconds float64) (*VideoSource, error) {
|
||||||
if fps <= 0 {
|
if fps <= 0 {
|
||||||
fps = 15
|
fps = 15
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user