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 expireTimestamp, err := strconv.ParseInt(expireString[1], 10, 64)
29 if err != nil {
30 log.Println("parseExpiration ERROR:", err)
31 return defaultCacheDuration
32 }
33
34 return time.Until(time.Unix(expireTimestamp, 0))
35}
36
37func getFormat(video youtube.Video, formatID int) *youtube.Format {
38 if formatID != 0 {
39 f := video.Formats.Select(formatsSelectFn)
40 l := len(f)
41 if l > 0 {
42 return &f[(formatID-1)%l]
43 }
44 }
45
46 f := video.Formats.Select(formatsSelectFnBest)
47 if len(f) > 0 {
48 return &f[0]
49 }
50
51 return nil
52}
53
54func getCaptions(video youtube.Video) map[string]Captions {
55 c := make(map[string]Captions)
56 for _, caption := range video.CaptionTracks {
57 c[caption.LanguageCode] = Captions{
58 VideoID: video.ID,
59 Language: caption.LanguageCode,
60 URL: caption.BaseURL,
61 }
62 }
63
64 return c
65}
66
67func formatsSelectFn(f youtube.Format) bool {
68 return f.AudioChannels > 1 && f.ContentLength < maxContentLength && strings.HasPrefix(f.MimeType, "video/mp4")
69}
70
71func formatsSelectFnBest(f youtube.Format) bool {
72 return f.AudioChannels > 1 && strings.HasPrefix(f.MimeType, "video/mp4")
73}
74
75func formatsSelectFnAudio(f youtube.Format) bool {
76 return f.QualityLabel == ""
77}
78
79func formatsSelectFnVideo(f youtube.Format) bool {
80 return !formatsSelectFnAudio(f)
81}
82
83func getURL(videoID string) string {
84 return fmt.Sprintf(fmtYouTubeURL, videoID)
85}
86
87func getFromCache(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
88 video, err = g.KS.Get(videoID)
89 if err != nil {
90 return
91 }
92
93 if video == nil {
94 err = errors.New("video should not be nil")
95 return
96 }
97
98 format = getFormat(*video, formatID)
99 return
100}
101
102func getFromYT(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
103 url := getURL(videoID)
104
105 log.Println("Requesting video", url)
106 video, err = g.YT.GetVideo(url)
107 if err != nil || video == nil {
108 return
109 }
110
111 format = getFormat(*video, formatID)
112 duration := defaultCacheDuration
113 if format != nil {
114 duration = parseExpiration(format.URL)
115 }
116
117 g.KS.Set(videoID, *video, duration)
118 return
119}
120
121func getVideo(videoID string, formatID int) (video *youtube.Video, format *youtube.Format, err error) {
122 video, format, err = getFromCache(videoID, formatID)
123 if err != nil {
124 video, format, err = getFromYT(videoID, formatID)
125 }
126 return
127}