-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathytlivechannel.go
154 lines (126 loc) · 3.36 KB
/
ytlivechannel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package bruxism
import (
"errors"
"strings"
"sync"
"time"
"google.golang.org/api/youtube/v3"
)
// YTLiveChannel is a monitor that will send new live videos to a provided channel.
type YTLiveChannel struct {
sync.RWMutex
service *youtube.Service
// map channelID -> chan
liveVideoChans map[string][]chan *youtube.Video
channelNames map[string]string
}
func NewYTLiveChannel(service *youtube.Service) *YTLiveChannel {
return &YTLiveChannel{
service: service,
channelNames: map[string]string{},
liveVideoChans: map[string][]chan *youtube.Video{},
}
}
// Monitor monitors a channel for new live videos and sends them down liveVideoChan.
// If the channel is live when this is called, it will not send the video down the channel.
func (y *YTLiveChannel) Monitor(channel string, liveVideoChan chan *youtube.Video) error {
y.Lock()
defer y.Unlock()
videoChans := y.liveVideoChans[channel]
for _, v := range videoChans {
if v == liveVideoChan {
return errors.New("already monitoring that channel")
}
}
created := len(y.liveVideoChans[channel]) == 0
y.liveVideoChans[channel] = append(y.liveVideoChans[channel], liveVideoChan)
if created {
go y.poll(channel)
}
return nil
}
// UnmonitorAll unmonitors a channel for live videos.
func (y *YTLiveChannel) Unmonitor(channel string, liveVideoChan chan *youtube.Video) error {
y.Lock()
defer y.Unlock()
videoChans := y.liveVideoChans[channel]
for i, v := range videoChans {
if v == liveVideoChan {
y.liveVideoChans[channel] = append(videoChans[:i], videoChans[i+1:]...)
return nil
}
}
return errors.New("channel not being monitored")
}
func (y *YTLiveChannel) ChannelName(channel string) string {
y.RLock()
defer y.RUnlock()
return y.channelNames[channel]
}
func (y *YTLiveChannel) poll(channel string) {
var lastAnnounce time.Time
seen := map[string]bool{}
first := true
for {
videos, _ := y.getLiveVideos(channel)
y.Lock()
for k, v := range videos {
if !seen[k] {
seen[k] = true
y.channelNames[channel] = v.Snippet.ChannelTitle
// Don't announce the videos that are already live.
if first {
continue
}
now := time.Now()
// Don't allow more than 1 announcement per hour.
if !now.After(lastAnnounce.Add(1 * time.Hour)) {
continue
}
lastAnnounce = now
for _, c := range y.liveVideoChans[channel] {
c <- v
}
}
}
if len(y.liveVideoChans[channel]) == 0 {
y.Unlock()
return
}
y.Unlock()
first = false
<-time.After(11 * time.Minute)
}
}
func (y *YTLiveChannel) getLiveVideos(channel string) (map[string]*youtube.Video, error) {
if y.service == nil {
return nil, errors.New("Service not available.")
}
search, err := y.service.Search.List([]string{"id"}).ChannelId(channel).EventType("live").Type("video").Do()
if err != nil {
return nil, err
}
ids := []string{}
for _, searchResult := range search.Items {
ids = append(ids, searchResult.Id.VideoId)
}
m := map[string]*youtube.Video{}
i := 0
for i < len(ids) {
next := i + 50
if next >= len(ids) {
next = len(ids)
}
videoList, err := y.service.Videos.List([]string{"id","snippet","liveStreamingDetails"}).MaxResults(50).Id(strings.Join(ids[i:next], ",")).Do()
if err != nil {
return nil, err
}
for _, v := range videoList.Items {
if v.LiveStreamingDetails.ActualEndTime == "" {
m[v.Id] = v
}
}
i = next
}
return m, nil
}