plugins/youtube.go (view raw)
1package plugins
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "net/url"
8 "strings"
9 "time"
10
11 "git.icyphox.sh/paprika/config"
12 "github.com/dustin/go-humanize"
13 "google.golang.org/api/option"
14 "google.golang.org/api/youtube/v3"
15 "gopkg.in/irc.v3"
16)
17
18type Youtube struct{}
19
20func (Youtube) Triggers() []string {
21 return []string{".yt"}
22}
23
24func (Youtube) Execute(m *irc.Message) (string, error) {
25 parsed := strings.SplitN(m.Trailing(), " ", 2)
26 if len(parsed) == 1 && parsed[0] == ".yt" {
27 return "usage: .yt QUERY", nil
28 } else if parsed[0] != ".yt" {
29 return "", NoReply // ???
30 }
31
32
33 return YoutubeSearch(parsed[1])
34}
35
36func init() {
37 Register(Youtube{})
38}
39
40var NoYtApiKey = errors.New("No Youtube Api Key")
41
42func constructApiService() (*youtube.Service, error) {
43 if apiKey, ok := config.C.ApiKeys["youtube"]; ok {
44 service, err := youtube.NewService(
45 context.TODO(),
46 option.WithAPIKey(apiKey),
47 option.WithUserAgent("github.com/icyphox/paprika"),
48 option.WithTelemetryDisabled(), // wtf?
49 )
50 return service, err
51 } else {
52 return nil, NoYtApiKey
53 }
54}
55
56func YoutubeSearch(query string) (string, error) {
57 service, err := constructApiService()
58 if err != nil {
59 return "", err
60 }
61
62 search := youtube.NewSearchService(service).List([]string{});
63 search = search.Q(query)
64 res, err := search.Do()
65 if err != nil {
66 return "", err
67 }
68
69 if len(res.Items) == 0 ||
70 res.Items[0].Id == nil ||
71 res.Items[0].Id.VideoId == "" {
72 return "[Youtube] No videos found.", nil
73 }
74 item := res.Items[0]
75
76 vid := item.Id.VideoId
77 println(vid)
78 description, err := YoutubeDescription(vid)
79 if err != nil {
80 return description, err
81 }
82
83 return fmt.Sprintf("%s - https://youtu.be/%s", description, vid), nil
84}
85
86func YoutubeDescriptionFromUrl(url *url.URL) (string, error) {
87 var vid string
88 if url.Host == "youtu.be" {
89 vid = url.Path[1:]
90 } else {
91 vid = url.Query().Get("v")
92 }
93
94 return YoutubeDescription(vid)
95}
96
97func YoutubeDescription(vid string) (string, error) {
98 if vid == "" {
99 return "[Youtube] Could not find video id.", nil
100 }
101
102 service, err := constructApiService()
103 if err != nil {
104 return "", err
105 }
106
107 vidservice := youtube.NewVideosService(service)
108 vcall := vidservice.List([]string{"snippet","statistics","contentDetails"});
109 vcall = vcall.Id(vid)
110 vres, err := vcall.Do()
111 if err != nil {
112 return "", err
113 }
114
115 if len(vres.Items) == 0 {
116 return "[Youtube] No video found", nil
117 }
118
119 snippet := vres.Items[0]
120 if snippet.Snippet == nil || snippet.ContentDetails == nil || snippet.Statistics == nil {
121 return "[Youtube] API Error. Required fields are nil.", nil
122 }
123
124 title := snippet.Snippet.Title
125 duration := strings.ToLower(snippet.ContentDetails.Duration[2:])
126 likes := humanize.Comma(int64(snippet.Statistics.LikeCount))
127 // disabled
128 // dislikes := humanize.Comma(int64(snippet.Statistics.DislikeCount))
129 views := humanize.Comma(int64(snippet.Statistics.ViewCount))
130 channelName := snippet.Snippet.ChannelTitle
131 publishedParsed, err := time.Parse(time.RFC3339, snippet.Snippet.PublishedAt)
132 var published string
133 if err == nil {
134 published = humanize.Time(publishedParsed)
135 } else {
136 published = snippet.Snippet.PublishedAt[:10]
137 }
138
139
140 return fmt.Sprintf(
141 "\x02%s\x02 %s - \x0303↑ %s\x03 \x0304↓ ?\x03 - \x02%s\x02 views - \x02%s\x02 %s",
142 title,
143 duration,
144 likes,
145 views,
146 channelName,
147 published,
148 ), nil
149}