1

I want to use youtube-dl exe file in Windows for downloading the videos to the client's browser with Golang Web App.

I have a page that contains an input for the website url (For example a youtube url) and I want to call youtube.dl exe file with this url in my server with Golang. But I could not download the file to the client's browser directly.

I don't want to download the video itself to my server. I want it to be downloaded to client's browser directly.

I tried many things on the web and here where I am. You can find my code snippet below.

func SearchHandler(w http.ResponseWriter, r *http.Request) {

// - --------------------------------------------------------------------------------------------------------------
// - Retrieve the HTML form parameter of POST method
// - --------------------------------------------------------------------------------------------------------------
url := r.FormValue("entry-domain")

logger.Printf("SearchHandler started to research the IP and MX data from %s domain", url)
fmt.Println("starting download................")
cmd := exec.Command("youtube-dl.exe", "-o", "-", url)
fmt.Println("downloading started...............")


out, err := cmd.CombinedOutput()
if err != nil {
    log.Fatalf("cmd.Run() failed with %s\n", err)
}

// //copy the relevant headers. If you want to preserve the downloaded file name, extract it with go's url parser.
w.Header().Set("Content-Disposition", "attachment; filename=BigBuckBunny.mp4")
w.Header().Set("Content-Type", "application/octet-stream")

//stream the body to the client without fully loading it into memory
reader := bytes.NewReader(out)
//w.Write(out)
io.Copy(w, reader)
fmt.Println("written to file.....................")
return}

I could download a file but it did not work as expected. I could not even open the file.

akinKaplanoglu
  • 728
  • 2
  • 8
  • 26
  • «I could not even open the file.» That's because you have no idea what happens when you run `youtube-dl` from your code, and that's _bad._ Have you at least try to glance into the tool's manual to understand what it's `-o` command-line option controls, and what passing the tool `-o -` actually enables? If you want to become proficient in programming, you _have to_ work on developing your problem-solving skills, and understanding what happens in _your_ code is a crucial thing for that. – kostix Jun 28 '19 at 12:36
  • 2
    @kostix, doesn't seem to me that they don't know what that flag does. The problem is almost certainly that stderr is intermingled with the video stream. Replacing CombinedOutput with Output would make this work, albeit horribly inefficiently. – Peter Jun 28 '19 at 12:41
  • @Peter, may be you're correct. I interpreted the «I could not even open the file» remark as "I failed to locate it on the server". Regarding your remark on `CombinedOutput` vs `Output`—why do you think doing that would be inefficient? In fact, I'd say that's the only way forward: you connect `Stdout` of the spawned process to the response writer and let Stderr be written somewhere (like `bytes.Buffer`), and then check its contents if the process fails. The contents may be used for logging or be sent to the user somehow. What do you think? – kostix Jun 28 '19 at 13:21
  • 1
    @kostix, it's inefficient because calling Output reads the whole video into memory before it can be written to the ResponseWriter. Writing Stderr to a buffer is a good idea though. I'll add that to my answer. – Peter Jun 28 '19 at 13:48
  • Hi @kostix, I do know what I am doing and what the command do. Of course I have tried these commands to see what youtube-dl is capable of. You make judgement and that is not ethical. – akinKaplanoglu Jun 28 '19 at 14:03
  • 1
    @AknKplnoglu, I see. Let me apologize! (But I hope my other suggestions to Peter helped to improve the resulting solution.) – kostix Jun 29 '19 at 11:43

1 Answers1

2

Simply assign the ResponseWriter to the command's Stdout field. I also suggest to use exec.CommandContext with the request context so that youtube-dl gets terminated quickly if the client aborts the request.

func SearchHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Disposition", "attachment; filename=BigBuckBunny.mp4")
    w.Header().Set("Content-Type", "application/octet-stream") 
    // or more precisely: w.Header().Set("Content-Type", "video/mp4") 

    url := r.FormValue("entry-domain")

    stderr := &bytes.Buffer{}

    cmd := exec.CommandContext(r.Context(), "youtube-dl.exe", "-o", "-", url)
    cmd.Stdout = w
    cmd.Stderr = stderr

    if err := cmd.Run(); err != nil {
        log.Println(err)
        log.Println("stderr:", buf.String())
    }
}
Peter
  • 29,454
  • 5
  • 48
  • 60