Skip to content

Commit d581cf2

Browse files
authored
Show extra information for youtube livestreams (#678)
1 parent 1893aee commit d581cf2

File tree

3 files changed

+69
-20
lines changed

3 files changed

+69
-20
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Breaking: Go version 1.22.1 is now the minimum required version to build this project. (#667, #671)
66
- Minor: Add playlist support to YouTube resolver. (#597, #601)
7+
- Minor: Add YouTube livestream support. (#678)
78
- Fix: Do not resolve /results using YouTube channel resolver (#616)
89

910
## 2.0.3

internal/resolvers/youtube/initialize.go

+10
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,23 @@ const (
4141
<br><b>Videos:</b> {{.VideoCount}}
4242
<br><b>Published:</b> {{.PublishedAt}}
4343
</div>
44+
`
45+
46+
youtubeStreamTooltip = `<div style="text-align: left;">
47+
<b>{{.Title}}</b>
48+
<br><b>Channel:</b> {{.ChannelTitle}}
49+
<br><b>Uptime:</b> {{.Uptime}}
50+
<br><b>Viewers:</b> {{.Viewers}}
51+
<br><b><span style="color: #ff0000;">Live</span></b>&nbsp;•&nbsp;<span style="color: #2ecc71;">{{.LikeCount}} likes</span>
52+
</div>
4453
`
4554
)
4655

4756
var (
4857
youtubeVideoTooltipTemplate = template.Must(template.New("youtubeVideoTooltip").Parse(youtubeVideoTooltip))
4958
youtubeChannelTooltipTemplate = template.Must(template.New("youtubeChannelTooltip").Parse(youtubeChannelTooltip))
5059
youtubePlaylistTooltipTemplate = template.Must(template.New("youtubePlaylistTooltip").Parse(youtubePlaylistTooltip))
60+
youtubeStreamTooltipTemplate = template.Must(template.New("youtubeStreamTooltip").Parse(youtubeStreamTooltip))
5161
)
5262

5363
func NewYouTubeVideoResolvers(ctx context.Context, cfg config.APIConfig, pool db.Pool, youtubeClient *youtubeAPI.Service) (resolver.Resolver, resolver.Resolver) {

internal/resolvers/youtube/video_loader.go

+58-20
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ type youtubeVideoTooltipData struct {
2525
AgeRestricted bool
2626
}
2727

28+
type youtubeStreamTooltipData struct {
29+
Title string
30+
ChannelTitle string
31+
Uptime string
32+
Viewers string
33+
LikeCount string
34+
}
35+
2836
type VideoLoader struct {
2937
youtubeClient *youtubeAPI.Service
3038
}
@@ -35,11 +43,13 @@ func (r *VideoLoader) Load(ctx context.Context, videoID string, req *http.Reques
3543
"statistics",
3644
"snippet",
3745
"contentDetails",
46+
"liveStreamingDetails",
3847
}
3948

4049
log.Debugw("[YouTube] Get video",
4150
"videoID", videoID,
4251
)
52+
4353
youtubeResponse, err := r.youtubeClient.Videos.List(youtubeVideoParts).Id(videoID).Do()
4454
if err != nil {
4555
return resolver.InternalServerErrorf("YouTube API error: %s", err)
@@ -58,32 +68,60 @@ func (r *VideoLoader) Load(ctx context.Context, videoID string, req *http.Reques
5868

5969
video := youtubeResponse.Items[0]
6070

61-
if video.ContentDetails == nil {
71+
if video.Snippet == nil {
6272
return resolver.InternalServerErrorf("YouTube video unavailable")
6373
}
6474

65-
// Check if a video is age resricted: https://stackoverflow.com/a/33750307
66-
var ageRestricted = false
67-
if video.ContentDetails.ContentRating != nil {
68-
if video.ContentDetails.ContentRating.YtRating == "ytAgeRestricted" {
69-
ageRestricted = true
75+
var tooltip bytes.Buffer
76+
77+
if video.Snippet.LiveBroadcastContent == "live" {
78+
if video.LiveStreamingDetails == nil {
79+
return resolver.InternalServerErrorf("YouTube livestream unavailable")
7080
}
71-
}
7281

73-
data := youtubeVideoTooltipData{
74-
Title: video.Snippet.Title,
75-
ChannelTitle: video.Snippet.ChannelTitle,
76-
Duration: humanize.DurationPT(video.ContentDetails.Duration),
77-
PublishDate: humanize.CreationDateRFC3339(video.Snippet.PublishedAt),
78-
Views: humanize.Number(video.Statistics.ViewCount),
79-
LikeCount: humanize.Number(video.Statistics.LikeCount),
80-
CommentCount: humanize.Number(video.Statistics.CommentCount),
81-
AgeRestricted: ageRestricted,
82-
}
82+
startTime, err := time.Parse(time.RFC3339, video.LiveStreamingDetails.ActualStartTime)
83+
if err != nil {
84+
return resolver.InternalServerErrorf("YouTube time parse error: %s", err)
85+
}
8386

84-
var tooltip bytes.Buffer
85-
if err := youtubeVideoTooltipTemplate.Execute(&tooltip, data); err != nil {
86-
return resolver.InternalServerErrorf("YouTube template error: %s", err)
87+
data := youtubeStreamTooltipData{
88+
Title: video.Snippet.Title,
89+
ChannelTitle: video.Snippet.ChannelTitle,
90+
Uptime: humanize.Duration(time.Since(startTime)),
91+
Viewers: humanize.Number(video.LiveStreamingDetails.ConcurrentViewers),
92+
LikeCount: humanize.Number(video.Statistics.LikeCount),
93+
}
94+
95+
if err := youtubeStreamTooltipTemplate.Execute(&tooltip, data); err != nil {
96+
return resolver.InternalServerErrorf("YouTube template error: %s", err)
97+
}
98+
} else {
99+
if video.ContentDetails == nil {
100+
return resolver.InternalServerErrorf("YouTube video unavailable")
101+
}
102+
103+
// Check if a video is age resricted: https://stackoverflow.com/a/33750307
104+
var ageRestricted = false
105+
if video.ContentDetails.ContentRating != nil {
106+
if video.ContentDetails.ContentRating.YtRating == "ytAgeRestricted" {
107+
ageRestricted = true
108+
}
109+
}
110+
111+
data := youtubeVideoTooltipData{
112+
Title: video.Snippet.Title,
113+
ChannelTitle: video.Snippet.ChannelTitle,
114+
Duration: humanize.DurationPT(video.ContentDetails.Duration),
115+
PublishDate: humanize.CreationDateRFC3339(video.Snippet.PublishedAt),
116+
Views: humanize.Number(video.Statistics.ViewCount),
117+
LikeCount: humanize.Number(video.Statistics.LikeCount),
118+
CommentCount: humanize.Number(video.Statistics.CommentCount),
119+
AgeRestricted: ageRestricted,
120+
}
121+
122+
if err := youtubeVideoTooltipTemplate.Execute(&tooltip, data); err != nil {
123+
return resolver.InternalServerErrorf("YouTube template error: %s", err)
124+
}
87125
}
88126

89127
thumbnail := video.Snippet.Thumbnails.Default.Url

0 commit comments

Comments
 (0)