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, }) }