all repos — gopipe @ 8e4f97e36220a7548c4bc0b8f16b6cd4bdd1ad83

Embed YouTube videos on Telegram, Discord and more!

src/app/video.go (view raw)

  1package app
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"log"
  7	"regexp"
  8	"strconv"
  9	"strings"
 10	"time"
 11
 12	g "github.com/birabittoh/gopipe/src/globals"
 13	"github.com/kkdai/youtube/v2"
 14)
 15
 16const (
 17	maxMB                = 20
 18	maxContentLength     = maxMB * 1048576
 19	defaultCacheDuration = 6 * time.Hour
 20)
 21
 22var (
 23	expireRegex = regexp.MustCompile(`(?i)expire=(\d+)`)
 24)
 25
 26func parseExpiration(url string) time.Duration {
 27	expireString := expireRegex.FindStringSubmatch(url)
 28	if len(expireString) < 2 {
 29		return defaultCacheDuration
 30	}
 31
 32	expireTimestamp, err := strconv.ParseInt(expireString[1], 10, 64)
 33	if err != nil {
 34		log.Println("parseExpiration ERROR:", err)
 35		return defaultCacheDuration
 36	}
 37
 38	return time.Until(time.Unix(expireTimestamp, 0))
 39}
 40
 41func getFormat(video youtube.Video, formatID int) *youtube.Format {
 42	if formatID != 0 {
 43		f := video.Formats.Select(formatsSelectFn)
 44		l := len(f)
 45		if l > 0 {
 46			return &f[(formatID-1)%l]
 47		}
 48	}
 49
 50	f := video.Formats.Select(formatsSelectFnBest)
 51	if len(f) > 0 {
 52		return &f[0]
 53	}
 54
 55	return nil
 56}
 57
 58func getCaptions(video youtube.Video) map[string]Captions {
 59	c := make(map[string]Captions)
 60	for _, caption := range video.CaptionTracks {
 61		c[caption.LanguageCode] = Captions{
 62			VideoID:  video.ID,
 63			Language: caption.LanguageCode,
 64			URL:      caption.BaseURL,
 65		}
 66	}
 67
 68	return c
 69}
 70
 71func formatsSelectFn(f youtube.Format) bool {
 72	return f.AudioChannels > 0 && f.ContentLength < maxContentLength && strings.HasPrefix(f.MimeType, "video/mp4")
 73}
 74
 75func formatsSelectFnBest(f youtube.Format) bool {
 76	return f.AudioChannels > 0 && strings.HasPrefix(f.MimeType, "video/mp4")
 77}
 78
 79func formatsSelectFnAudioVideo(f youtube.Format) bool {
 80	return f.AudioChannels > 0 && f.QualityLabel != ""
 81}
 82
 83func formatsSelectFnVideo(f youtube.Format) bool {
 84	return f.QualityLabel != "" && f.AudioChannels == 0
 85}
 86
 87func formatsSelectFnAudio(f youtube.Format) bool {
 88	return f.QualityLabel == ""
 89}
 90
 91func getURL(videoID string) string {
 92	return fmt.Sprintf(fmtYouTubeURL, videoID)
 93}
 94
 95func getFromCache(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
 96	video, err = g.KS.Get(videoID)
 97	if err != nil {
 98		return
 99	}
100
101	if video == nil {
102		err = errors.New("video should not be nil")
103		return
104	}
105
106	format = getFormat(*video, formatID)
107	return
108}
109
110func getFromYT(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
111	url := getURL(videoID)
112
113	log.Println("Requesting video", url)
114	video, err = g.YT.GetVideo(url)
115	if err != nil || video == nil {
116		return
117	}
118
119	format = getFormat(*video, formatID)
120	duration := defaultCacheDuration
121	if format != nil {
122		duration = parseExpiration(format.URL)
123	}
124
125	g.KS.Set(videoID, *video, duration)
126	return
127}
128
129func getVideo(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
130	video, format, err = getFromCache(videoID, formatID)
131	if err != nil {
132		video, format, err = getFromYT(videoID, formatID)
133	}
134	return
135}