Support video counter queries for YouTube

This commit is contained in:
Maksym Pavlenko
2019-03-29 19:53:23 -07:00
parent 35ad8c3f43
commit 16010dcd63
3 changed files with 67 additions and 13 deletions
+4
View File
@@ -24,3 +24,7 @@ func makeEnclosure(feed *model.Feed, id string, lengthInBytes int64) (string, it
url := fmt.Sprintf("http://podsync.net/download/%s/%s.%s", feed.HashID, id, ext)
return url, contentType, lengthInBytes
}
type VideoCounter interface {
GetVideoCount(feed *model.Feed) (uint64, error)
}
+33 -9
View File
@@ -25,6 +25,8 @@ const (
highAudioBytesPerSecond = 128000 / 8
)
var _ VideoCounter = (*YouTubeBuilder)(nil)
type apiKey string
func (key apiKey) Get() (string, string) {
@@ -38,8 +40,8 @@ type YouTubeBuilder struct {
// Cost: 5 units (call method: 1, snippet: 2, contentDetails: 2)
// See https://developers.google.com/youtube/v3/docs/channels/list#part
func (yt *YouTubeBuilder) listChannels(linkType api.LinkType, id string) (*youtube.Channel, error) {
req := yt.client.Channels.List("id,snippet,contentDetails")
func (yt *YouTubeBuilder) listChannels(linkType api.LinkType, id string, parts string) (*youtube.Channel, error) {
req := yt.client.Channels.List(parts)
switch linkType {
case api.LinkTypeChannel:
@@ -65,8 +67,8 @@ func (yt *YouTubeBuilder) listChannels(linkType api.LinkType, id string) (*youtu
// Cost: 3 units (call method: 1, snippet: 2)
// See https://developers.google.com/youtube/v3/docs/playlists/list#part
func (yt *YouTubeBuilder) listPlaylists(id, channelID string) (*youtube.Playlist, error) {
req := yt.client.Playlists.List("id,snippet")
func (yt *YouTubeBuilder) listPlaylists(id, channelID string, parts string) (*youtube.Playlist, error) {
req := yt.client.Playlists.List(parts)
if id != "" {
req = req.Id(id)
@@ -132,9 +134,29 @@ func (yt *YouTubeBuilder) selectThumbnail(snippet *youtube.ThumbnailDetails, qua
return snippet.Default.Url
}
// Cost:
// - 5 units for channel or user
// - 3 units for playlist
func (yt *YouTubeBuilder) GetVideoCount(feed *model.Feed) (uint64, error) {
switch feed.LinkType {
case api.LinkTypeChannel, api.LinkTypeUser:
// Cost: 3 units
if channel, err := yt.listChannels(feed.LinkType, feed.ItemID, "id,statistics"); err != nil {
return 0, err
} else {
return channel.Statistics.VideoCount, nil
}
case api.LinkTypePlaylist:
// Cost: 3 units
if playlist, err := yt.listPlaylists(feed.ItemID, "", "id,contentDetails"); err != nil {
return 0, err
} else {
return uint64(playlist.ContentDetails.ItemCount), nil
}
default:
return 0, errors.New("unsupported link format")
}
}
func (yt *YouTubeBuilder) queryFeed(feed *model.Feed) (*itunes.Podcast, string, error) {
var (
title string
@@ -147,7 +169,8 @@ func (yt *YouTubeBuilder) queryFeed(feed *model.Feed) (*itunes.Podcast, string,
switch feed.LinkType {
case api.LinkTypeChannel, api.LinkTypeUser:
channel, err := yt.listChannels(feed.LinkType, feed.ItemID)
// Cost: 5 units for channel or user
channel, err := yt.listChannels(feed.LinkType, feed.ItemID, "id,snippet,contentDetails")
if err != nil {
return nil, "", err
}
@@ -172,7 +195,8 @@ func (yt *YouTubeBuilder) queryFeed(feed *model.Feed) (*itunes.Podcast, string,
thumbnails = channel.Snippet.Thumbnails
case api.LinkTypePlaylist:
playlist, err := yt.listPlaylists(feed.ItemID, "")
// Cost: 3 units for playlist
playlist, err := yt.listPlaylists(feed.ItemID, "", "id,snippet")
if err != nil {
return nil, "", err
}
+30 -4
View File
@@ -13,7 +13,7 @@ import (
var ytKey = os.Getenv("YOUTUBE_TEST_API_KEY")
func TestQueryYTChannel(t *testing.T) {
func TestYT_QueryChannel(t *testing.T) {
if ytKey == "" {
t.Skip("YouTube API key is not provided")
}
@@ -21,16 +21,16 @@ func TestQueryYTChannel(t *testing.T) {
builder, err := NewYouTubeBuilder(ytKey)
require.NoError(t, err)
channel, err := builder.listChannels(api.LinkTypeChannel, "UC2yTVSttx7lxAOAzx1opjoA")
channel, err := builder.listChannels(api.LinkTypeChannel, "UC2yTVSttx7lxAOAzx1opjoA", "id")
require.NoError(t, err)
require.Equal(t, "UC2yTVSttx7lxAOAzx1opjoA", channel.Id)
channel, err = builder.listChannels(api.LinkTypeUser, "fxigr1")
channel, err = builder.listChannels(api.LinkTypeUser, "fxigr1", "id")
require.NoError(t, err)
require.Equal(t, "UCr_fwF-n-2_olTYd-m3n32g", channel.Id)
}
func TestBuildYTFeed(t *testing.T) {
func TestYT_BuildFeed(t *testing.T) {
if ytKey == "" {
t.Skip("YouTube API key is not provided")
}
@@ -82,3 +82,29 @@ func TestBuildYTFeed(t *testing.T) {
})
}
}
func TestYT_GetVideoCount(t *testing.T) {
if ytKey == "" {
t.Skip("YouTube API key is not provided")
}
builder, err := NewYouTubeBuilder(ytKey)
require.NoError(t, err)
feeds := []*model.Feed{
{Provider: api.ProviderYoutube, LinkType: api.LinkTypeUser, ItemID: "fxigr1"},
{Provider: api.ProviderYoutube, LinkType: api.LinkTypeChannel, ItemID: "UCupvZG-5ko_eiXAupbDfxWw"},
{Provider: api.ProviderYoutube, LinkType: api.LinkTypePlaylist, ItemID: "PLfVk3KMh3VX1yJShGRsJmsqAjvMIviJYQ"},
{Provider: api.ProviderYoutube, LinkType: api.LinkTypeChannel, ItemID: "UCK9lZ2lHRBgx2LOcqPifukA"},
{Provider: api.ProviderYoutube, LinkType: api.LinkTypeUser, ItemID: "WylsaLive"},
{Provider: api.ProviderYoutube, LinkType: api.LinkTypePlaylist, ItemID: "PLUVl5pafUrBydT_gsCjRGeCy0hFHloec8"},
}
for _, f := range feeds {
t.Run(f.ItemID, func(t *testing.T) {
count, err := builder.GetVideoCount(f)
assert.NoError(t, err)
assert.NotZero(t, count)
})
}
}