feat: direct download with filename:

- Fixed issues with escaping query in Hx-Redirect
	header.

	- GetFilename functions that runs concurrently
	since it takes time for yt-dlp to fetch info.
	Investigate getting url and name in one execution.

	- Directly redirects to /download-direct for a
	streaming response of the file. I am observing
	that the request on the server side to content
	provider does not wait for the full download.
	instead, it is streamed in a buffer, that is
	also copied at the same time to the user response
	as partial. Pretty cool. Not much memory overhead,
	which was the main concern.

Signed-off-by: HeshamTB <hishaminv@gmail.com>
This commit is contained in:
HeshamTB 2023-09-14 21:49:17 +03:00
parent 61502ea2f6
commit 4a1a254026
2 changed files with 49 additions and 14 deletions

35
main.go
View File

@ -208,7 +208,12 @@ func main() {
log.Println("User requested audio")
}
filenameChan := make(chan string)
go GetContentFilename(userURL, filenameChan)
downloadURL, err := getYoutubeDownloadURL(userURL)
filename := <- filenameChan
if err != nil {
log.Println(err.Error())
ctx.StatusCode = 500
@ -218,7 +223,13 @@ func main() {
}
ctx.DownloadURL = downloadURL
w.Header().Add("Hx-Redirect", fmt.Sprintf("/download-direct?URL=%s", ctx.DownloadURL))
w.Header().Add(
"Hx-Redirect",
fmt.Sprintf("/download-direct?URL=%s&filename=%s",
url.QueryEscape(ctx.DownloadURL),
url.QueryEscape(filename)),
)
err = templates.ExecuteTemplate(w,"download-result.html", ctx)
if err != nil {
log.Println(err.Error())
@ -240,17 +251,12 @@ func main() {
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
userURL := strings.Trim(r.URL.Query().Get("URL"), "\n")
filename := strings.Trim(r.URL.Query().Get("filename"), "\n")
if userURL == "" {
log.Println("Empty URL")
w.WriteHeader(400)
ctx.StatusCode = 400
if err := templates.ExecuteTemplate(w,"download-result.html", ctx); err != nil {
@ -258,11 +264,11 @@ func main() {
}
return
}
log.Println("Got url: ", userURL, "for direct download")
ctx.DownloadURL = userURL
req, err := http.NewRequest("GET", ctx.DownloadURL, nil)
if err != nil {
log.Println(err.Error())
ctx.StatusCode = 500
ctx.Err = &err
if err := templates.ExecuteTemplate(w,"download-result.html", ctx); err != nil {
@ -286,13 +292,14 @@ func main() {
}
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.Header().Set(
"Content-Disposition",
fmt.Sprintf("attachment;filename=%s", filename),
)
w.WriteHeader(206)
if dataRequest.ContentLength == 0 {

28
yt.go
View File

@ -43,6 +43,34 @@ func getYoutubeDownloadURL(link string) (string, error) {
return dlLink, nil
}
// Get the content filename with the extension. If not possible,
// and empty string is sent to c
func GetContentFilename(link string, c chan string) {
var filename string
params := make([]string, 0)
params = append(params, "--no-playlist", "--get-title")
if isProbablyYT(link) {
params = append(params, ytlinkParams...)
}
params = append(params, link)
cmd := exec.Command("yt-dlp", params...)
result, err := cmd.Output()
if err != nil {
c <- ""
}
filename = string(result)
c <- filename
}
func isValidURL(data string) bool {
_, err := url.ParseRequestURI(data)