From 4f1137e1625c75aa0b0499e03ccc16e90e497043 Mon Sep 17 00:00:00 2001 From: Chigozirim Igweamaka Date: Wed, 9 Jul 2025 12:35:08 +0100 Subject: [PATCH] feat: add downloadYTaudio2 function to download YouTube audio using yt-dlp CLI --- server/spotify/youtube.go | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/server/spotify/youtube.go b/server/spotify/youtube.go index 51d5d04..e62fb51 100644 --- a/server/spotify/youtube.go +++ b/server/spotify/youtube.go @@ -4,6 +4,13 @@ import ( "context" "fmt" "log" + "log/slog" + "os" + "os/exec" + "path/filepath" + "song-recognition/utils" + + ytdlib "github.com/kkdai/youtube/v2" "errors" "io" @@ -215,3 +222,91 @@ func ytSearch(searchTerm string, limit int) (results []*SearchResult, err error) return results, nil } + +// downloadYTaudio1 downloads audio from a YouTube video using the github.com/kkdai/youtube library. +func downloadYTaudio1(id, outputFilePath string) (string, error) { + logger := utils.GetLogger() + + dir := filepath.Dir(outputFilePath) + if stat, err := os.Stat(dir); err != nil || !stat.IsDir() { + logger.Error("Invalid directory for output file", slog.Any("error", err)) + return "", errors.New("output directory does not exist or is not a directory") + } + + client := ytdlib.Client{} + video, err := client.GetVideo(id) + if err != nil { + logger.Error("Error getting YouTube video", slog.Any("error", err)) + return "", err + } + + /* + itag code: 140, container: m4a, content: audio, bitrate: 128k + change the FindByItag parameter to 139 if you want smaller files (but with a bitrate of 48k) + https://gist.github.com/sidneys/7095afe4da4ae58694d128b1034e01e2 + */ + formats := video.Formats.Itag(140) + + /* in some cases, when attempting to download the audio + using the library github.com/kkdai/youtube, + the download fails (and shows the file size as 0 bytes) + until the second or third attempt. */ + var fileSize int64 + file, err := os.Create(outputFilePath) + if err != nil { + logger.Error("Error creating file", slog.Any("error", err)) + return "", err + } + + for fileSize == 0 { + stream, _, err := client.GetStream(video, &formats[0]) + if err != nil { + logger.Error("Error getting stream", slog.Any("error", err)) + return "", err + } + + if _, err = io.Copy(file, stream); err != nil { + logger.Error("Error copying stream to file", slog.Any("error", err)) + return "", err + } + + fileSize, _ = GetFileSize(outputFilePath) + } + defer file.Close() + + return outputFilePath + "m4a", nil +} + +// downloadYTaudio2 downloads audio from a YouTube video using yt-dlp command line tool. +func downloadYTaudio2(videoURL, outputFilePath string) (string, error) { + logger := utils.GetLogger() + + dir := filepath.Dir(outputFilePath) + if stat, err := os.Stat(dir); err != nil || !stat.IsDir() { + logger.Error("Invalid directory for output file", slog.Any("error", err)) + return "", errors.New("output directory does not exist or is not a directory") + } + + _, err := exec.LookPath("yt-dlp") + if err != nil { + logger.Error("yt-dlp not found in PATH", slog.Any("error", err)) + return "", errors.New("yt-dlp is not installed or not in PATH") + } + + audioFmt := "wav" + cmd := exec.Command( + "yt-dlp", + "-f", "bestaudio", + "--extract-audio", + "--audio-format", audioFmt, + "-o", outputFilePath, + videoURL, + ) + + output, err := cmd.CombinedOutput() + if err != nil { + logger.Error("yt-dlp command failed", slog.String("output", string(output)), slog.Any("error", err)) + return "", err + } + return outputFilePath + "." + audioFmt, nil +}