all repos — gopipe @ 72f5236b15f0ae2fd00b01b36a7bce52dbd2c429

Embed YouTube videos on Telegram, Discord and more!

add download form
Marco Andronaco andronacomarco@gmail.com
Fri, 18 Oct 2024 16:17:47 +0200
commit

72f5236b15f0ae2fd00b01b36a7bce52dbd2c429

parent

5a91c6dec0ac3e5f50ca3c46af1228c0d8120223

M src/app/handlers.gosrc/app/handlers.go

@@ -95,15 +95,17 @@ videoURL = fmt.Sprintf("/proxy/%s/%d", videoID, formatID)

} data := map[string]interface{}{ - "VideoID": videoID, - "VideoURL": videoURL, - "Author": video.Author, - "Title": video.Title, - "Description": video.Description, - "Thumbnail": thumbnail, - "Duration": video.Duration, - "Captions": getCaptions(*video), - "Heading": template.HTML(heading), + "VideoID": videoID, + "VideoURL": videoURL, + "Author": video.Author, + "Title": video.Title, + "Description": video.Description, + "Thumbnail": thumbnail, + "Duration": video.Duration, + "Captions": getCaptions(*video), + "Heading": template.HTML(heading), + "VideoFormats": video.Formats.Select(formatsSelectFnVideo), + "AudioFormats": video.Formats.Select(formatsSelectFnAudio), } err = g.XT.ExecuteTemplate(w, "video.tmpl", data)

@@ -112,6 +114,40 @@ log.Println("indexHandler ERROR:", err)

http.Error(w, err500, http.StatusInternalServerError) return } +} + +func downloadHandler(w http.ResponseWriter, r *http.Request) { + videoID := r.FormValue("video") + if videoID == "" { + http.Error(w, "Missing video ID", http.StatusBadRequest) + return + } + + if !videoRegex.MatchString(videoID) { + log.Println("Invalid video ID:", videoID) + http.Error(w, err404, http.StatusNotFound) + return + } + + itagno := r.FormValue("itagno") + if itagno == "" { + http.Error(w, "Missing ItagNo", http.StatusBadRequest) + return + } + + video, err := g.KS.Get(videoID) + if err != nil || video == nil { + http.Error(w, err404, http.StatusNotFound) + return + } + + formats := video.Formats.Quality(itagno) + if len(formats) == 0 { + http.Error(w, err404, http.StatusNotFound) + return + } + + http.Redirect(w, r, formats[0].URL, http.StatusFound) } func cacheHandler(w http.ResponseWriter, r *http.Request) {
M src/app/main.gosrc/app/main.go

@@ -65,6 +65,8 @@ r.HandleFunc("GET /proxy/{videoID}", proxyHandler)

r.HandleFunc("GET /proxy/{videoID}/{formatID}", proxyHandler) r.HandleFunc("GET /sub/{videoID}/{language}", subHandler) + r.HandleFunc("POST /download", downloadHandler) + r.HandleFunc("GET /cache", cacheHandler) log.Println("Serving on port " + g.Port)
M src/app/video.gosrc/app/video.go

@@ -72,6 +72,14 @@ func formatsSelectFnBest(f youtube.Format) bool {

return f.AudioChannels > 1 && strings.HasPrefix(f.MimeType, "video/mp4") } +func formatsSelectFnAudio(f youtube.Format) bool { + return f.QualityLabel == "" +} + +func formatsSelectFnVideo(f youtube.Format) bool { + return !formatsSelectFnAudio(f) +} + func getURL(videoID string) string { return fmt.Sprintf(fmtYouTubeURL, videoID) }
M src/globals/globals.gosrc/globals/globals.go

@@ -2,7 +2,11 @@ package globals

import ( "bytes" + "fmt" + "html/template" "net/http" + "strconv" + "strings" "time" "github.com/birabittoh/myks"

@@ -17,11 +21,45 @@ Port string

C = http.DefaultClient YT = youtube.Client{} - XT = extemplate.New() + XT = extemplate.New().Funcs(funcMap) KS = myks.New[youtube.Video](3 * time.Hour) PKS *myks.KeyStore[bytes.Buffer] AdminUser string AdminPass string + + funcMap = template.FuncMap{"parseFormat": parseFormat} ) + +func parseFormat(f youtube.Format) (res string) { + isAudio := f.QualityLabel == "" + + if isAudio { + bitrate := f.AverageBitrate + if bitrate == 0 { + bitrate = f.Bitrate + } + res = strconv.Itoa(bitrate/1000) + "kbps" + } else { + res = f.QualityLabel + } + + mime := strings.Split(f.MimeType, ";") + res += " - " + mime[0] + + codecs := " (" + strings.Split(mime[1], "\"")[1] + ")" + + if isAudio { + return res + " - audio only" + codecs + } + + res += fmt.Sprintf(" (%d FPS)", f.FPS) + + if f.AudioChannels == 0 { + res += " - video only" + } + + res += codecs + return +}
M templates/index.tmpltemplates/index.tmpl

@@ -17,7 +17,7 @@

<section> <header> <h3 style="margin-bottom: 4px">How to use</h3> - <p>Replace <code>www.youtube.com</code> or <code>youtu.be</code> with <span id="changeme">this domain</span> to fix embeds for short videos.</p> + <p>Replace <code>www.youtube.com</code> or <code>youtu.be</code> with <noscript id="changeme">this domain</noscript> to fix embeds for short videos.</p> </header> <video src="https://github.com/birabittoh/FixYouTube-legacy/assets/26506860/2896d39e-a86e-47ce-939a-785b73d11683"
M templates/video.tmpltemplates/video.tmpl

@@ -33,6 +33,22 @@ </video>

<h2>{{ .Title }}</h2> <h3>&gt; {{ .Author }}</h3> <pre style="white-space: pre-wrap">{{ .Description }}</pre> + <form action="/download" method="post" rel="noopener" target="_blank" style="display: grid; grid-template-columns: auto auto; justify-content: space-between;"> + <input type="hidden" name="video" value="{{ .VideoID }}"> + <select name="itagno"> + {{ range .VideoFormats }} + <option value="{{ .ItagNo }}"> + {{ parseFormat . }} + </option> + {{ end }} + {{ range .AudioFormats }} + <option value="{{ .ItagNo }}"> + {{ parseFormat . }} + </option> + {{ end }} + </select> + <button type="submit">Download</button> + </form> <a href="https://www.youtube.com/watch?v={{ .VideoID }}">Watch on YouTube</a> <br /> <a href="/">What is this?</a>