package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
func isAcceptableError(err error) bool {
if exitErr, ok := err.(*exec.ExitError); ok {
return exitErr.ExitCode() == 1
}
return false
}
func runYtdlp(args []string) ([]byte, error) {
cmd := exec.Command(cfg.YtdlpPath, args...)
out, err := cmd.Output()
if err != nil && !isAcceptableError(err) {
return nil, fmt.Errorf("yt-dlp error: %w", err)
}
decoder := simplifiedchinese.GBK.NewDecoder()
decoded, _, decErr := transform.Bytes(decoder, out)
if decErr != nil {
return out, nil
}
return decoded, nil
}
func runYtdlpStreaming(args []string) error {
cmd := exec.Command(cfg.YtdlpPath, args...)
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
return err
}
reader := transform.NewReader(stdout, simplifiedchinese.GBK.NewDecoder())
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
err = cmd.Wait()
if err != nil && !isAcceptableError(err) {
return fmt.Errorf("yt-dlp error: %w", err)
}
return nil
}
func ListPlaylistTracks(olakID string) ([]Track, error) {
out, err := runYtdlp([]string{
"--flat-playlist",
"--print", "%(playlist_index)s\t%(title)s\t%(id)s",
"https://www.youtube.com/playlist?list=" + olakID,
})
if err != nil {
return nil, err
}
var tracks []Track
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.SplitN(line, "\t", 3)
if len(parts) < 3 {
continue
}
idx, _ := strconv.Atoi(parts[0])
tracks = append(tracks, Track{
Index: idx,
Title: parts[1],
VideoID: parts[2],
})
}
return tracks, nil
}
func DownloadPlaylist(olakID, outputDir string) error {
return runYtdlpStreaming([]string{
"-x", "--audio-quality", "0",
"--embed-thumbnail", "--embed-metadata",
"-o", outputDir + "/%(playlist_index)s. %(title)s.%(ext)s",
"https://www.youtube.com/playlist?list=" + olakID,
})
}
func DownloadSingle(videoID, outputDir string) error {
return runYtdlpStreaming([]string{
"-x", "--audio-quality", "0",
"--embed-thumbnail", "--embed-metadata",
"-o", outputDir + "/%(title)s.%(ext)s",
"https://www.youtube.com/watch?v=" + videoID,
})
}