all repos — gopipe @ d9ab51a35a4242ee8f1f5c0760ced3905f84e872

Embed YouTube videos on Telegram, Discord and more!

add cache
Marco Andronaco andronacomarco@gmail.com
Tue, 15 Oct 2024 22:50:46 +0200
commit

d9ab51a35a4242ee8f1f5c0760ced3905f84e872

parent

28aff114e23cb584fea3a9c94c0836f4cb521eca

M DockerfileDockerfile

@@ -13,7 +13,7 @@ COPY src ./src

COPY *.go ./ # Build -RUN CGO_ENABLED=0 go build -trimpath -o /dist/fixyoutube +RUN CGO_ENABLED=0 go build -trimpath -o /dist/gopipe # Test FROM build-stage AS run-test-stage

@@ -27,4 +27,4 @@ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

COPY --from=builder /dist . COPY templates ./templates -ENTRYPOINT ["./fixyoutube"] +ENTRYPOINT ["./gopipe"]
M README.mdREADME.md

@@ -1,4 +1,4 @@

-# FixYouTube +# GoPipe Embed YouTube videos on Telegram, Discord and more! ## How to use:
M docker-compose.simple.yamldocker-compose.simple.yaml

@@ -2,7 +2,7 @@ services:

app: build: . image: ghcr.io/birabittoh/gopipe:main - container_name: fixyoutube + container_name: gopipe restart: unless-stopped ports: - 3000:3000
M docker-compose.yamldocker-compose.yaml

@@ -2,7 +2,7 @@ services:

app: build: . image: ghcr.io/birabittoh/gopipe:main - container_name: fixyoutube + container_name: gopipe restart: unless-stopped env_file: - .env

@@ -15,7 +15,7 @@ env_file:

- docker/swag.env volumes: #- /etc/config/swag:/config - - ./docker/fixyoutube.subdomain.conf:/config/nginx/proxy-confs/fixyoutube.subdomain.conf:ro + - ./docker/gopipe.subdomain.conf:/config/nginx/proxy-confs/gopipe.subdomain.conf:ro ports: - 443:443 - 80:80
M src/app/handlers.gosrc/app/handlers.go

@@ -10,8 +10,11 @@

g "github.com/birabittoh/gopipe/src/globals" ) -const err500 = "Internal Server Error" -const urlDuration = 6 * time.Hour +const ( + fmtYouTubeURL = "https://www.youtube.com/watch?v=%s" + err500 = "Internal Server Error" + urlDuration = 6 * time.Hour +) var ( templates = template.Must(template.ParseGlob("templates/*.html"))

@@ -38,13 +41,11 @@ return

} } - url := "https://www.youtube.com/watch?v=" + videoID - if !userAgentRegex.MatchString(r.UserAgent()) { - log.Println("Regex did not match.") + log.Println("Regex did not match. UA: ", r.UserAgent()) if !g.Debug { - log.Println("Redirecting. UA:", r.UserAgent()) - http.Redirect(w, r, url, http.StatusFound) + log.Println("Redirecting.") + http.Redirect(w, r, getURL(videoID), http.StatusFound) return } }

@@ -55,26 +56,15 @@ http.Error(w, "Invalid video ID.", http.StatusBadRequest)

return } - video, err := g.YT.GetVideo(url) + video, format, err := getVideo(videoID) if err != nil { - log.Println("videoHandler ERROR: ", err) http.Error(w, err500, http.StatusInternalServerError) return } - formats := video.Formats.WithAudioChannels() - if len(formats) == 0 { - log.Println("videoHandler ERROR: ", err) - http.Error(w, err500, http.StatusInternalServerError) - return - } - - // TODO: check formats[i].ContentLength - // g.KS.Set(videoID, formats[0].URL, urlDuration) - data := map[string]interface{}{ "VideoID": videoID, - "VideoURL": formats[0].URL, + "VideoURL": format.URL, "Uploader": video.Author, "Title": video.Title, "Description": video.Description,
A src/app/video.go

@@ -0,0 +1,99 @@

+package app + +import ( + "errors" + "fmt" + "log" + "regexp" + "strconv" + "time" + + g "github.com/birabittoh/gopipe/src/globals" + "github.com/kkdai/youtube/v2" +) + +const ( + maxMB = 20 + maxContentLength = maxMB * 1048576 + defaultCacheDuration = 6 * time.Hour +) + +var ( + emptyVideo = youtube.Video{} + expireRegex = regexp.MustCompile(`(?i)expire=(\d+)`) +) + +func parseExpiration(url string) (time.Duration, error) { + expireString := expireRegex.FindStringSubmatch(url) + expireTimestamp, err := strconv.ParseInt(expireString[1], 10, 64) + if err != nil { + log.Println("parseExpiration ERROR: ", err) + return time.Duration(0), err + } + + return time.Until(time.Unix(expireTimestamp, 0)), nil +} + +func formatsSelectFn(f youtube.Format) bool { + return f.AudioChannels > 1 && f.ContentLength < maxContentLength +} + +func getURL(videoID string) string { + return fmt.Sprintf(fmtYouTubeURL, videoID) +} + +func getFromCache(videoID string) (video *youtube.Video, format youtube.Format, err error) { + video, err = g.KS.Get(videoID) + if err != nil { + return + } + + if video == nil { + err = errors.New("video should not be nil") + return + } + + if video.ID == emptyVideo.ID { + err = errors.New("no formats for this video") + return + } + + formats := video.Formats.Select(formatsSelectFn) + if len(formats) == 0 { + err = errors.New("no formats for this video") + return + } + + format = formats[0] + return +} + +func getFromYT(videoID string) (video *youtube.Video, format youtube.Format, err error) { + video, err = g.YT.GetVideo(getURL(videoID)) + if err != nil { + return + } + + formats := video.Formats.Select(formatsSelectFn) + if len(formats) == 0 { + g.KS.Set(videoID, emptyVideo, defaultCacheDuration) + return + } + + format = formats[0] + expiration, err := parseExpiration(format.URL) + if err != nil { + expiration = defaultCacheDuration + } + + g.KS.Set(videoID, *video, expiration) + return +} + +func getVideo(videoID string) (video *youtube.Video, format youtube.Format, err error) { + video, format, err = getFromCache(videoID) + if err != nil { + video, format, err = getFromYT(videoID) + } + return +}
M src/globals/globals.gosrc/globals/globals.go

@@ -1,6 +1,8 @@

package globals import ( + "time" + "github.com/birabittoh/myks" "github.com/kkdai/youtube/v2" )

@@ -10,5 +12,5 @@ Debug bool

Port string YT = youtube.Client{} - KS = myks.New[string](0) + KS = myks.New[youtube.Video](3 * time.Hour) )
M swag/fixyoutube.subdomain.confswag/fixyoutube.subdomain.conf

@@ -1,5 +1,5 @@

-# make sure that your app container is named fixyoutube -# make sure that your dns has a cname set for fixyoutube +# make sure that your app container is named gopipe +# make sure that your dns has a cname set for gopipe server { listen 443 ssl;

@@ -15,7 +15,7 @@

location / { include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; - set $upstream_app fixyoutube; + set $upstream_app gopipe; set $upstream_port 3000; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port;
M templates/index.htmltemplates/index.html

@@ -4,10 +4,10 @@

<head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <meta property="og:title" content="FixYouTube" /> - <meta property="og:site_name" content="FixYouTube" /> + <meta property="og:title" content="GoTube" /> + <meta property="og:site_name" content="GoTube" /> <meta property="og:description" content="Embed YouTube videos on Telegram, Discord and more!" /> - <title>FixYouTube</title> + <title>GoTube</title> <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text fill=%22white%22 y=%22.9em%22 font-size=%2290%22>🛠</text></svg>">

@@ -17,10 +17,10 @@

<body> <main class="container" style="max-width: 35rem"> <hgroup> - <h1>FixYouTube</h1> + <h1>GoTube</h1> <h2>Embed YouTube videos on Telegram, Discord and more!</h2> </hgroup> - <p>FixYouTube serves fixed YouTube video embeds. Heavily inspired by <a href="https://fxtwitter.com">fxtwitter.com</a> and <a href="https://ddinstagram.com">ddinstagram.com</a>.</p> + <p>GoTube serves fixed YouTube video embeds. Heavily inspired by <a href="https://fxtwitter.com">fxtwitter.com</a> and <a href="https://ddinstagram.com">ddinstagram.com</a>.</p> <section> <header>
M templates/video.htmltemplates/video.html

@@ -1,10 +1,10 @@

<!DOCTYPE html> <!-- -███████ ██ ██ ██ ██ ██ ███ ██ ██ ██████ ██ ██ ██████ ███████ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -█████ ██ ███ ████ ██ ██ ██ ██ ██ ██ ██ ██████ █████ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -██ ██ ██ ██ ██ ███ ███ ██ ███ ██████ ███████ +███████ ██ ██ ██ ██ ██ ███ ██ ██ ██████ ██ ██ ██████ ███████ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +█████ ██ ███ ████ ██ ██ ██ ██ ██ ██ ██ ██████ █████ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ███ ███ ██ ███ ██████ ███████ ██ ██ A better way to embed YouTube videos on Telegram (inspired by FixTweet). ██ -->

@@ -21,7 +21,7 @@ <meta property="twitter:creator" content="{{ .Uploader }}" />

<meta property="twitter:title" content="{{ .Title }}" /> <meta property="og:title" content="{{ .Title }}" /> <meta property="og:description" content="{{ .Description }}" /> - <meta property="og:site_name" content="FixYouTube ({{ .Uploader }})" /> + <meta property="og:site_name" content="GoTube ({{ .Uploader }})" /> <meta property="twitter:image" content="0" /> <meta property="twitter:player:stream:content_type" content="video/mp4" /> {{ if .VideoURL }}