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 emptyVideo = youtube.Video{}
24 expireRegex = regexp.MustCompile(`(?i)expire=(\d+)`)
25)
26
27func parseExpiration(url string) time.Duration {
28 expireString := expireRegex.FindStringSubmatch(url)
29 expireTimestamp, err := strconv.ParseInt(expireString[1], 10, 64)
30 if err != nil {
31 log.Println("parseExpiration ERROR: ", err)
32 return defaultCacheDuration
33 }
34
35 return time.Until(time.Unix(expireTimestamp, 0))
36}
37
38func getFormat(video youtube.Video, formatID int) *youtube.Format {
39 formats := video.Formats.Select(formatsSelectFn)
40 l := len(formats)
41 if l == 0 {
42 return nil
43 }
44
45 return &formats[formatID%l]
46}
47
48func formatsSelectFn(f youtube.Format) bool {
49 return f.AudioChannels > 1 && f.ContentLength < maxContentLength && strings.HasPrefix(f.MimeType, "video/mp4")
50}
51
52func getURL(videoID string) string {
53 return fmt.Sprintf(fmtYouTubeURL, videoID)
54}
55
56func getFromCache(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
57 video, err = g.KS.Get(videoID)
58 if err != nil {
59 return
60 }
61
62 if video == nil {
63 err = errors.New("video should not be nil")
64 return
65 }
66
67 if video.ID == emptyVideo.ID {
68 err = errors.New("no formats for this video")
69 return
70 }
71
72 format = getFormat(*video, formatID)
73 return
74}
75
76func getFromYT(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
77 url := getURL(videoID)
78
79 log.Println("Requesting video ", url)
80 video, err = g.YT.GetVideo(url)
81 if err != nil {
82 return
83 }
84
85 format = getFormat(*video, formatID)
86 duration := defaultCacheDuration
87 v := emptyVideo
88 if format != nil {
89 v = *video
90 duration = parseExpiration(format.URL)
91 }
92
93 g.KS.Set(videoID, v, duration)
94 return
95}
96
97func getVideo(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
98 video, format, err = getFromCache(videoID, formatID)
99 if err != nil {
100 video, format, err = getFromYT(videoID, formatID)
101 }
102 return
103}