diff --git a/cmd/podsync/updater.go b/cmd/podsync/updater.go index 30cc6f9..ecadabc 100644 --- a/cmd/podsync/updater.go +++ b/cmd/podsync/updater.go @@ -33,7 +33,7 @@ func (u *Updater) Update(ctx context.Context, feed *config.Feed) error { } // Query API to get episodes - result, err := provider.Build(feed) + result, err := provider.Build(ctx, feed) if err != nil { return err } @@ -47,7 +47,7 @@ func (u *Updater) Update(ctx context.Context, feed *config.Feed) error { // Save XML to disk xmlName := fmt.Sprintf("%s.xml", result.ItemID) xmlPath := filepath.Join(u.config.Server.DataDir, xmlName) - if err := ioutil.WriteFile(xmlPath, []byte(podcast.String()), 600); err != nil { + if err := ioutil.WriteFile(xmlPath, []byte(podcast.String()), 0600); err != nil { return errors.Wrapf(err, "failed to write XML feed to disk") } diff --git a/pkg/builder/common.go b/pkg/builder/common.go index af66f7e..de5bfe9 100644 --- a/pkg/builder/common.go +++ b/pkg/builder/common.go @@ -1,6 +1,7 @@ package builder import ( + "context" "errors" "github.com/mxpv/podsync/pkg/config" @@ -13,5 +14,5 @@ var ( ) type Builder interface { - Build(cfg *config.Feed) (*model.Feed, error) + Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) } diff --git a/pkg/builder/vimeo.go b/pkg/builder/vimeo.go index eaf637c..77ee68a 100644 --- a/pkg/builder/vimeo.go +++ b/pkg/builder/vimeo.go @@ -158,7 +158,7 @@ func (v *VimeoBuilder) queryVideos(getVideos getVideosFunc, feed *model.Feed) er } } -func (v *VimeoBuilder) Build(cfg *config.Feed) (*model.Feed, error) { +func (v *VimeoBuilder) Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) { info, err := link.Parse(cfg.URL) if err != nil { return nil, err diff --git a/pkg/builder/youtube.go b/pkg/builder/youtube.go index 2a66f25..1577b2a 100644 --- a/pkg/builder/youtube.go +++ b/pkg/builder/youtube.go @@ -1,6 +1,7 @@ package builder import ( + "context" "fmt" "net/http" "sort" @@ -38,7 +39,7 @@ 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 link.Type, id string, parts string) (*youtube.Channel, error) { +func (yt *YouTubeBuilder) listChannels(ctx context.Context, linkType link.Type, id string, parts string) (*youtube.Channel, error) { req := yt.client.Channels.List(parts) switch linkType { @@ -50,7 +51,7 @@ func (yt *YouTubeBuilder) listChannels(linkType link.Type, id string, parts stri return nil, errors.New("unsupported link type") } - resp, err := req.Do(yt.key) + resp, err := req.Context(ctx).Do(yt.key) if err != nil { return nil, errors.Wrapf(err, "failed to query channel") } @@ -65,7 +66,7 @@ func (yt *YouTubeBuilder) listChannels(linkType link.Type, id string, parts stri // 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, parts string) (*youtube.Playlist, error) { +func (yt *YouTubeBuilder) listPlaylists(ctx context.Context, id, channelID string, parts string) (*youtube.Playlist, error) { req := yt.client.Playlists.List(parts) if id != "" { @@ -74,7 +75,7 @@ func (yt *YouTubeBuilder) listPlaylists(id, channelID string, parts string) (*yo req = req.ChannelId(channelID) } - resp, err := req.Do(yt.key) + resp, err := req.Context(ctx).Do(yt.key) if err != nil { return nil, errors.Wrapf(err, "failed to query playlist") } @@ -89,13 +90,13 @@ func (yt *YouTubeBuilder) listPlaylists(id, channelID string, parts string) (*yo // Cost: 3 units (call: 1, snippet: 2) // See https://developers.google.com/youtube/v3/docs/playlistItems/list#part -func (yt *YouTubeBuilder) listPlaylistItems(itemID string, pageToken string) ([]*youtube.PlaylistItem, string, error) { +func (yt *YouTubeBuilder) listPlaylistItems(ctx context.Context, itemID string, pageToken string) ([]*youtube.PlaylistItem, string, error) { req := yt.client.PlaylistItems.List("id,snippet").MaxResults(maxYoutubeResults).PlaylistId(itemID) if pageToken != "" { req = req.PageToken(pageToken) } - resp, err := req.Do(yt.key) + resp, err := req.Context(ctx).Do(yt.key) if err != nil { return nil, "", errors.Wrap(err, "failed to query playlist items") } @@ -141,11 +142,11 @@ func (yt *YouTubeBuilder) selectThumbnail(snippet *youtube.ThumbnailDetails, qua return snippet.Default.Url } -func (yt *YouTubeBuilder) GetVideoCount(info *link.Info) (uint64, error) { +func (yt *YouTubeBuilder) GetVideoCount(ctx context.Context, info *link.Info) (uint64, error) { switch info.LinkType { case link.TypeChannel, link.TypeUser: // Cost: 3 units - if channel, err := yt.listChannels(info.LinkType, info.ItemID, "id,statistics"); err != nil { + if channel, err := yt.listChannels(ctx, info.LinkType, info.ItemID, "id,statistics"); err != nil { return 0, err } else { // nolint:golint return channel.Statistics.VideoCount, nil @@ -153,7 +154,7 @@ func (yt *YouTubeBuilder) GetVideoCount(info *link.Info) (uint64, error) { case link.TypePlaylist: // Cost: 3 units - if playlist, err := yt.listPlaylists(info.ItemID, "", "id,contentDetails"); err != nil { + if playlist, err := yt.listPlaylists(ctx, info.ItemID, "", "id,contentDetails"); err != nil { return 0, err } else { // nolint:golint return uint64(playlist.ContentDetails.ItemCount), nil @@ -164,7 +165,7 @@ func (yt *YouTubeBuilder) GetVideoCount(info *link.Info) (uint64, error) { } } -func (yt *YouTubeBuilder) queryFeed(feed *model.Feed, info *link.Info) error { +func (yt *YouTubeBuilder) queryFeed(ctx context.Context, feed *model.Feed, info *link.Info) error { var ( thumbnails *youtube.ThumbnailDetails ) @@ -172,7 +173,7 @@ func (yt *YouTubeBuilder) queryFeed(feed *model.Feed, info *link.Info) error { switch info.LinkType { case link.TypeChannel, link.TypeUser: // Cost: 5 units for channel or user - channel, err := yt.listChannels(info.LinkType, info.ItemID, "id,snippet,contentDetails") + channel, err := yt.listChannels(ctx, info.LinkType, info.ItemID, "id,snippet,contentDetails") if err != nil { return err } @@ -200,7 +201,7 @@ func (yt *YouTubeBuilder) queryFeed(feed *model.Feed, info *link.Info) error { case link.TypePlaylist: // Cost: 3 units for playlist - playlist, err := yt.listPlaylists(info.ItemID, "", "id,snippet") + playlist, err := yt.listPlaylists(ctx, info.ItemID, "", "id,snippet") if err != nil { return err } @@ -258,14 +259,14 @@ func (yt *YouTubeBuilder) getSize(duration int64, feed *model.Feed) int64 { // Cost: 5 units (call: 1, snippet: 2, contentDetails: 2) // See https://developers.google.com/youtube/v3/docs/videos/list#part -func (yt *YouTubeBuilder) queryVideoDescriptions(playlist map[string]*youtube.PlaylistItemSnippet, feed *model.Feed) error { +func (yt *YouTubeBuilder) queryVideoDescriptions(ctx context.Context, playlist map[string]*youtube.PlaylistItemSnippet, feed *model.Feed) error { // Make the list of video ids ids := make([]string, 0, len(playlist)) for _, s := range playlist { ids = append(ids, s.ResourceId.VideoId) } - req, err := yt.client.Videos.List("id,snippet,contentDetails").Id(strings.Join(ids, ",")).Do(yt.key) + req, err := yt.client.Videos.List("id,snippet,contentDetails").Id(strings.Join(ids, ",")).Context(ctx).Do(yt.key) if err != nil { return errors.Wrap(err, "failed to query video descriptions") } @@ -326,14 +327,14 @@ func (yt *YouTubeBuilder) queryVideoDescriptions(playlist map[string]*youtube.Pl } // Cost: (3 units + 5 units) * X pages = 8 units per page -func (yt *YouTubeBuilder) queryItems(feed *model.Feed) error { +func (yt *YouTubeBuilder) queryItems(ctx context.Context, feed *model.Feed) error { var ( token string count int ) for { - items, pageToken, err := yt.listPlaylistItems(feed.ItemID, token) + items, pageToken, err := yt.listPlaylistItems(ctx, feed.ItemID, token) if err != nil { return err } @@ -352,7 +353,7 @@ func (yt *YouTubeBuilder) queryItems(feed *model.Feed) error { } // Query video descriptions from the list of ids - if err := yt.queryVideoDescriptions(snippets, feed); err != nil { + if err := yt.queryVideoDescriptions(ctx, snippets, feed); err != nil { return err } @@ -362,7 +363,7 @@ func (yt *YouTubeBuilder) queryItems(feed *model.Feed) error { } } -func (yt *YouTubeBuilder) Build(cfg *config.Feed) (*model.Feed, error) { +func (yt *YouTubeBuilder) Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) { info, err := link.Parse(cfg.URL) if err != nil { return nil, err @@ -380,11 +381,11 @@ func (yt *YouTubeBuilder) Build(cfg *config.Feed) (*model.Feed, error) { } // Query general information about feed (title, description, lang, etc) - if err := yt.queryFeed(feed, &info); err != nil { + if err := yt.queryFeed(ctx, feed, &info); err != nil { return nil, err } - if err := yt.queryItems(feed); err != nil { + if err := yt.queryItems(ctx, feed); err != nil { return nil, err } diff --git a/pkg/builder/youtube_test.go b/pkg/builder/youtube_test.go index fe6c498..853f756 100644 --- a/pkg/builder/youtube_test.go +++ b/pkg/builder/youtube_test.go @@ -1,6 +1,7 @@ package builder import ( + "context" "os" "testing" @@ -11,7 +12,10 @@ import ( "github.com/mxpv/podsync/pkg/link" ) -var ytKey = os.Getenv("YOUTUBE_TEST_API_KEY") +var ( + testCtx = context.Background() + ytKey = os.Getenv("YOUTUBE_TEST_API_KEY") +) func TestYT_QueryChannel(t *testing.T) { if ytKey == "" { @@ -21,11 +25,11 @@ func TestYT_QueryChannel(t *testing.T) { builder, err := NewYouTubeBuilder(ytKey) require.NoError(t, err) - channel, err := builder.listChannels(link.TypeChannel, "UC2yTVSttx7lxAOAzx1opjoA", "id") + channel, err := builder.listChannels(testCtx, link.TypeChannel, "UC2yTVSttx7lxAOAzx1opjoA", "id") require.NoError(t, err) require.Equal(t, "UC2yTVSttx7lxAOAzx1opjoA", channel.Id) - channel, err = builder.listChannels(link.TypeUser, "fxigr1", "id") + channel, err = builder.listChannels(testCtx, link.TypeUser, "fxigr1", "id") require.NoError(t, err) require.Equal(t, "UCr_fwF-n-2_olTYd-m3n32g", channel.Id) } @@ -49,7 +53,7 @@ func TestYT_BuildFeed(t *testing.T) { for _, addr := range urls { t.Run(addr, func(t *testing.T) { - feed, err := builder.Build(&config.Feed{URL: addr}) + feed, err := builder.Build(testCtx, &config.Feed{URL: addr}) require.NoError(t, err) assert.NotEmpty(t, feed.Title) @@ -91,7 +95,7 @@ func TestYT_GetVideoCount(t *testing.T) { for _, f := range feeds { feed := f t.Run(f.ItemID, func(t *testing.T) { - count, err := builder.GetVideoCount(feed) + count, err := builder.GetVideoCount(testCtx, feed) assert.NoError(t, err) assert.NotZero(t, count) })