From 61502ea2f605aaa16816d776c15aed5faf3837ae Mon Sep 17 00:00:00 2001 From: HeshamTB Date: Thu, 14 Sep 2023 20:13:32 +0300 Subject: [PATCH] feat: direct download via streaming --- main.go | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/main.go b/main.go index 354f133..486c3e7 100644 --- a/main.go +++ b/main.go @@ -2,10 +2,14 @@ package main import ( "encoding/json" + "fmt" "html/template" + "io" "log" "net/http" + "net/url" "os" + "strings" "time" ) @@ -121,6 +125,25 @@ func NewContext(r *http.Request) *Context { } } +func handleToAudio(ctx *Context, w http.ResponseWriter) http.ResponseWriter { + + log.Println("User requested audio") + w.WriteHeader(400) + + jsonMsg, err := json.Marshal( + apiMessageResponse{ + Message: "Audio only is not implemented", + }, + ) + if err != nil { + log.Println(err.Error()) + w.WriteHeader(500) + } + w.Write(jsonMsg) + + return w +} + func init() { log.Println("[ init ] Starting...") @@ -195,6 +218,7 @@ func main() { } ctx.DownloadURL = downloadURL + w.Header().Add("Hx-Redirect", fmt.Sprintf("/download-direct?URL=%s", ctx.DownloadURL)) err = templates.ExecuteTemplate(w,"download-result.html", ctx) if err != nil { log.Println(err.Error()) @@ -206,6 +230,86 @@ func main() { }, ) + handler.HandleFunc( + "/download-direct", + func(w http.ResponseWriter, r *http.Request) { + + ctx := NewContext(r) + if r.Method != "GET" { + w.WriteHeader(400) + return + } + + userURL := r.URL.Query().Get("URL") + urlRaw := strings.TrimLeft(r.URL.RawQuery, "URL=") + urlRaw, err := url.QueryUnescape(urlRaw) + if err != nil { + log.Println("Can not unescape url") + w.WriteHeader(400) + return + } + userURL = urlRaw + + if userURL == "" { + w.WriteHeader(400) + ctx.StatusCode = 400 + if err := templates.ExecuteTemplate(w,"download-result.html", ctx); err != nil { + log.Println(err.Error()) + } + return + } + log.Println("Got url: ", userURL, "for direct download") + ctx.DownloadURL = userURL + + req, err := http.NewRequest("GET", ctx.DownloadURL, nil) + if err != nil { + ctx.StatusCode = 500 + ctx.Err = &err + if err := templates.ExecuteTemplate(w,"download-result.html", ctx); err != nil { + log.Println(err.Error()) + } + return + } + req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0") + + client := http.Client{} + + dataRequest, err := client.Do(req) + if err != nil { + log.Println(err.Error()) + ctx.StatusCode = 500 + ctx.Err = &err + if err := templates.ExecuteTemplate(w,"download-result.html", ctx); err != nil { + log.Println(err.Error()) + } + return + } + defer dataRequest.Body.Close() + + log.Printf("HTTP Client response: %v", dataRequest) + + if dataRequest.StatusCode != 200 { + log.Println("Failed to get content for URL", userURL) + } + + w.Header().Set("Content-Disposition", "attachment;filename=") + w.WriteHeader(206) + + if dataRequest.ContentLength == 0 { + log.Println("Empty body from content url") + w.WriteHeader(500) + return + } + + n, err := io.Copy(w, dataRequest.Body) + if err != nil { + log.Println(err.Error()) + } + log.Printf("Copied %d bytes", n) + }, + ) + + handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { ctx := NewContext(r) formats := []DownloadFormats{}