From 82bbd02cdfa04b8ebd0a6684bbe70ac2a39d19b9 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Mon, 20 Apr 2020 18:38:52 -0700 Subject: [PATCH 1/6] Switch toml package to github.com/naoina/toml --- go.mod | 4 +++- go.sum | 6 ++++++ pkg/config/config.go | 34 ++++++++++++++-------------------- pkg/config/config_test.go | 2 +- pkg/config/toml.go | 19 +++++++++++++++++++ 5 files changed, 43 insertions(+), 22 deletions(-) create mode 100644 pkg/config/toml.go diff --git a/go.mod b/go.mod index 7cc468f7..1aa09325 100644 --- a/go.mod +++ b/go.mod @@ -2,13 +2,15 @@ module github.com/mxpv/podsync require ( github.com/BrianHicks/finch v0.0.0-20140409222414-419bd73c29ec - github.com/BurntSushi/toml v0.3.1 github.com/dgraph-io/badger v1.6.0 github.com/eduncan911/podcast v1.4.2 github.com/gilliek/go-opml v1.0.0 github.com/golang/mock v1.4.3 github.com/hashicorp/go-multierror v1.0.0 github.com/jessevdk/go-flags v1.4.0 + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/naoina/go-stringutil v0.1.0 // indirect + github.com/naoina/toml v0.1.1 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 github.com/silentsokolov/go-vimeo v0.0.0-20190116124215-06829264260c diff --git a/go.sum b/go.sum index ca804f43..55670fe9 100644 --- a/go.sum +++ b/go.sum @@ -37,9 +37,15 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8= +github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/pkg/config/config.go b/pkg/config/config.go index 25e3d313..6f289df2 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -2,11 +2,11 @@ package config import ( "fmt" + "io/ioutil" "path/filepath" - "time" - "github.com/BurntSushi/toml" "github.com/hashicorp/go-multierror" + "github.com/naoina/toml" "github.com/pkg/errors" "github.com/mxpv/podsync/pkg/model" @@ -44,6 +44,11 @@ type Feed struct { OPML bool `toml:"opml"` } +type Filters struct { + Title string `toml:"title"` + // More filters to be added here +} + type Custom struct { CoverArt string `toml:"cover_art"` Category string `toml:"category"` @@ -125,10 +130,14 @@ type Config struct { // LoadConfig loads TOML configuration from a file path func LoadConfig(path string) (*Config, error) { - config := Config{} - _, err := toml.DecodeFile(path, &config) + data, err := ioutil.ReadFile(path) if err != nil { - return nil, errors.Wrap(err, "failed to load config file") + return nil, errors.Wrapf(err, "failed to read config file: %s", path) + } + + config := Config{} + if err := toml.Unmarshal(data, &config); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal toml") } for id, feed := range config.Feeds { @@ -207,18 +216,3 @@ func (c *Config) applyDefaults(configPath string) { } } } - -type Duration struct { - time.Duration -} - -func (d *Duration) UnmarshalText(text []byte) error { - var err error - d.Duration, err = time.ParseDuration(string(text)) - return err -} - -type Filters struct { - Title string `toml:"title"` - // More filters to be added here -} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 908ae293..7660615b 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -44,7 +44,7 @@ self_update = true config, err := LoadConfig(path) assert.NoError(t, err) - assert.NotNil(t, config) + require.NotNil(t, config) assert.Equal(t, "test/data/", config.Server.DataDir) assert.EqualValues(t, 80, config.Server.Port) diff --git a/pkg/config/toml.go b/pkg/config/toml.go new file mode 100644 index 00000000..474a98cc --- /dev/null +++ b/pkg/config/toml.go @@ -0,0 +1,19 @@ +package config + +import ( + "time" +) + +type Duration struct { + time.Duration +} + +func (d *Duration) UnmarshalText(text []byte) error { + res, err := time.ParseDuration(string(text)) + if err != nil { + return err + } + + *d = Duration{res} + return nil +} From 2623a30b838ad066d3c2e57d6e0dc5d4bb0dcf52 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Mon, 20 Apr 2020 18:53:28 -0700 Subject: [PATCH 2/6] Allow specify list of API keys in TOML --- pkg/config/config.go | 4 ++-- pkg/config/config_test.go | 34 +++++++++++++++++++++++++++++++--- pkg/config/toml.go | 22 ++++++++++++++++++++++ pkg/feed/common.go | 4 ++-- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 6f289df2..9e43e5a5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -59,10 +59,10 @@ type Custom struct { type Tokens struct { // YouTube API key. // See https://developers.google.com/youtube/registering_an_application - YouTube string `toml:"youtube"` + YouTube StringSlice `toml:"youtube"` // Vimeo developer key. // See https://developer.vimeo.com/api/guides/start#generate-access-token - Vimeo string `toml:"vimeo"` + Vimeo StringSlice `toml:"vimeo"` } type Server struct { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 7660615b..b2372018 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -16,7 +16,10 @@ func TestLoadConfig(t *testing.T) { const file = ` [tokens] youtube = "123" -vimeo = "321" +vimeo = [ + "321", + "456" +] [server] port = 80 @@ -51,8 +54,11 @@ self_update = true assert.Equal(t, "/home/user/db/", config.Database.Dir) - assert.Equal(t, "123", config.Tokens.YouTube) - assert.Equal(t, "321", config.Tokens.Vimeo) + require.Len(t, config.Tokens.YouTube, 1) + assert.Equal(t, "123", config.Tokens.YouTube[0]) + require.Len(t, config.Tokens.Vimeo, 2) + assert.Equal(t, "321", config.Tokens.Vimeo[0]) + assert.Equal(t, "456", config.Tokens.Vimeo[1]) assert.Len(t, config.Feeds, 1) feed, ok := config.Feeds["XYZ"] @@ -75,6 +81,28 @@ self_update = true assert.True(t, config.Downloader.SelfUpdate) } +func TestLoadEmptyKeyList(t *testing.T) { + const file = ` +[tokens] +vimeo = [] + +[server] +data_dir = "/data" +[feeds] + [feeds.A] + url = "https://youtube.com/watch?v=ygIUF678y40" +` + path := setup(t, file) + defer os.Remove(path) + + config, err := LoadConfig(path) + assert.NoError(t, err) + require.NotNil(t, config) + + require.Len(t, config.Tokens.YouTube, 0) + require.Len(t, config.Tokens.Vimeo, 0) +} + func TestApplyDefaults(t *testing.T) { const file = ` [server] diff --git a/pkg/config/toml.go b/pkg/config/toml.go index 474a98cc..bdcf7717 100644 --- a/pkg/config/toml.go +++ b/pkg/config/toml.go @@ -2,6 +2,8 @@ package config import ( "time" + + "github.com/pkg/errors" ) type Duration struct { @@ -17,3 +19,23 @@ func (d *Duration) UnmarshalText(text []byte) error { *d = Duration{res} return nil } + +// StringSlice is a toml extension that lets you to specify either a string +// value (a slice with just one element) or a string slice. +type StringSlice []string + +func (s *StringSlice) UnmarshalTOML(decode func(interface{}) error) error { + var single string + if err := decode(&single); err == nil { + *s = []string{single} + return nil + } + + var slice []string + if err := decode(&slice); err == nil { + *s = slice + return nil + } + + return errors.New("failed to decode string (slice) field") +} diff --git a/pkg/feed/common.go b/pkg/feed/common.go index eca7be6b..debaf51d 100644 --- a/pkg/feed/common.go +++ b/pkg/feed/common.go @@ -26,9 +26,9 @@ func New(ctx context.Context, cfg *config.Feed, tokens config.Tokens) (Builder, switch info.Provider { case model.ProviderYoutube: - provider, err = NewYouTubeBuilder(tokens.YouTube) + provider, err = NewYouTubeBuilder(tokens.YouTube[0]) case model.ProviderVimeo: - provider, err = NewVimeoBuilder(ctx, tokens.Vimeo) + provider, err = NewVimeoBuilder(ctx, tokens.Vimeo[0]) default: return nil, errors.Errorf("unsupported provider %q", info.Provider) } From 7a65e3d1c5639094a0b0f73a1a9305797f206e60 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Mon, 20 Apr 2020 19:15:30 -0700 Subject: [PATCH 3/6] Implement key providers --- pkg/feed/key.go | 51 ++++++++++++++++++++++++++++++++++++++++++++ pkg/feed/key_test.go | 26 ++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 pkg/feed/key.go create mode 100644 pkg/feed/key_test.go diff --git a/pkg/feed/key.go b/pkg/feed/key.go new file mode 100644 index 00000000..8063b3cd --- /dev/null +++ b/pkg/feed/key.go @@ -0,0 +1,51 @@ +package feed + +import ( + "sync" + + "github.com/pkg/errors" +) + +type KeyProvider interface { + Get() string +} + +type FixedKeyProvider struct { + key string +} + +func NewFixedKey(key string) (KeyProvider, error) { + if key == "" { + return nil, errors.New("key can't be empty") + } + + return &FixedKeyProvider{key: key}, nil +} + +func (p FixedKeyProvider) Get() string { + return p.key +} + +type RotatedKeyProvider struct { + keys []string + lock sync.Mutex + index int +} + +func NewRotatedKeys(keys []string) (KeyProvider, error) { + if len(keys) < 2 { + return nil, errors.Errorf("at least 2 keys required (got %d)", len(keys)) + } + + return &RotatedKeyProvider{keys: keys, index: 0}, nil +} + +func (p *RotatedKeyProvider) Get() string { + p.lock.Lock() + defer p.lock.Unlock() + + current := p.index % len(p.keys) + p.index++ + + return p.keys[current] +} diff --git a/pkg/feed/key_test.go b/pkg/feed/key_test.go new file mode 100644 index 00000000..5cd776f0 --- /dev/null +++ b/pkg/feed/key_test.go @@ -0,0 +1,26 @@ +package feed + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewFixedKey(t *testing.T) { + key, err := NewFixedKey("123") + assert.NoError(t, err) + + assert.EqualValues(t, "123", key.Get()) + assert.EqualValues(t, "123", key.Get()) +} + +func TestNewRotatedKeys(t *testing.T) { + key, err := NewRotatedKeys([]string{"123", "456"}) + assert.NoError(t, err) + + assert.EqualValues(t, "123", key.Get()) + assert.EqualValues(t, "456", key.Get()) + + assert.EqualValues(t, "123", key.Get()) + assert.EqualValues(t, "456", key.Get()) +} From d1bd2c2d246f8def3c80811e9cebe608e7b2a16b Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Mon, 20 Apr 2020 20:54:49 -0700 Subject: [PATCH 4/6] Integrate key providers --- cmd/podsync/updater.go | 14 +++++++++++++- pkg/config/config.go | 11 +---------- pkg/config/config_test.go | 14 +++++++------- pkg/feed/common.go | 18 ++++++++---------- pkg/feed/key.go | 11 +++++++++++ 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/cmd/podsync/updater.go b/cmd/podsync/updater.go index db425c7e..37d45409 100644 --- a/cmd/podsync/updater.go +++ b/cmd/podsync/updater.go @@ -31,14 +31,26 @@ type Updater struct { downloader Downloader db db.Storage fs fs.Storage + keys map[model.Provider]feed.KeyProvider } func NewUpdater(config *config.Config, downloader Downloader, db db.Storage, fs fs.Storage) (*Updater, error) { + keys := map[model.Provider]feed.KeyProvider{} + + for name, list := range config.Tokens { + provider, err := feed.NewKeyProvider(list) + if err != nil { + return nil, errors.Wrapf(err, "failed to create key provider for %q", name) + } + keys[name] = provider + } + return &Updater{ config: config, downloader: downloader, db: db, fs: fs, + keys: keys, }, nil } @@ -79,7 +91,7 @@ func (u *Updater) Update(ctx context.Context, feedConfig *config.Feed) error { // updateFeed pulls API for new episodes and saves them to database func (u *Updater) updateFeed(ctx context.Context, feedConfig *config.Feed) error { // Create an updater for this feed type - provider, err := feed.New(ctx, feedConfig, u.config.Tokens) + provider, err := feed.New(ctx, feedConfig, u.keys) if err != nil { return err } diff --git a/pkg/config/config.go b/pkg/config/config.go index 9e43e5a5..22414971 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -56,15 +56,6 @@ type Custom struct { Language string `toml:"lang"` } -type Tokens struct { - // YouTube API key. - // See https://developers.google.com/youtube/registering_an_application - YouTube StringSlice `toml:"youtube"` - // Vimeo developer key. - // See https://developer.vimeo.com/api/guides/start#generate-access-token - Vimeo StringSlice `toml:"vimeo"` -} - type Server struct { // Hostname to use for download links Hostname string `toml:"hostname"` @@ -123,7 +114,7 @@ type Config struct { // ID will be used as feed ID in http://podsync.net/{FEED_ID}.xml Feeds map[string]*Feed // Tokens is API keys to use to access YouTube/Vimeo APIs. - Tokens Tokens `toml:"tokens"` + Tokens map[model.Provider]StringSlice `toml:"tokens"` // Downloader (youtube-dl) configuration Downloader Downloader `toml:"downloader"` } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index b2372018..177a08cd 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -54,11 +54,11 @@ self_update = true assert.Equal(t, "/home/user/db/", config.Database.Dir) - require.Len(t, config.Tokens.YouTube, 1) - assert.Equal(t, "123", config.Tokens.YouTube[0]) - require.Len(t, config.Tokens.Vimeo, 2) - assert.Equal(t, "321", config.Tokens.Vimeo[0]) - assert.Equal(t, "456", config.Tokens.Vimeo[1]) + require.Len(t, config.Tokens["youtube"], 1) + assert.Equal(t, "123", config.Tokens["youtube"][0]) + require.Len(t, config.Tokens["vimeo"], 2) + assert.Equal(t, "321", config.Tokens["vimeo"][0]) + assert.Equal(t, "456", config.Tokens["vimeo"][1]) assert.Len(t, config.Feeds, 1) feed, ok := config.Feeds["XYZ"] @@ -99,8 +99,8 @@ data_dir = "/data" assert.NoError(t, err) require.NotNil(t, config) - require.Len(t, config.Tokens.YouTube, 0) - require.Len(t, config.Tokens.Vimeo, 0) + require.Len(t, config.Tokens, 1) + require.Len(t, config.Tokens["vimeo"], 0) } func TestApplyDefaults(t *testing.T) { diff --git a/pkg/feed/common.go b/pkg/feed/common.go index debaf51d..5f75def3 100644 --- a/pkg/feed/common.go +++ b/pkg/feed/common.go @@ -13,27 +13,25 @@ type Builder interface { Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) } -func New(ctx context.Context, cfg *config.Feed, tokens config.Tokens) (Builder, error) { - var ( - provider Builder - err error - ) - +func New(ctx context.Context, cfg *config.Feed, keys map[model.Provider]KeyProvider) (Builder, error) { info, err := ParseURL(cfg.URL) if err != nil { return nil, err } + keyProvider, ok := keys[info.Provider] + if !ok { + return nil, errors.Errorf("unknown key provider: %s", info.Provider) + } + switch info.Provider { case model.ProviderYoutube: - provider, err = NewYouTubeBuilder(tokens.YouTube[0]) + return NewYouTubeBuilder(keyProvider.Get()) case model.ProviderVimeo: - provider, err = NewVimeoBuilder(ctx, tokens.Vimeo[0]) + return NewVimeoBuilder(ctx, keyProvider.Get()) default: return nil, errors.Errorf("unsupported provider %q", info.Provider) } - - return provider, err } type feedProvider interface { diff --git a/pkg/feed/key.go b/pkg/feed/key.go index 8063b3cd..5872b92c 100644 --- a/pkg/feed/key.go +++ b/pkg/feed/key.go @@ -10,6 +10,17 @@ type KeyProvider interface { Get() string } +func NewKeyProvider(keys []string) (KeyProvider, error) { + switch len(keys) { + case 0: + return nil, errors.New("no keys") + case 1: + return NewFixedKey(keys[0]) + default: + return NewRotatedKeys(keys) + } +} + type FixedKeyProvider struct { key string } From 24c9e7953dcd3991a371d5e2438db8f9566c9379 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Mon, 20 Apr 2020 21:02:05 -0700 Subject: [PATCH 5/6] Refactor feed package --- cmd/podsync/updater.go | 13 +++++- pkg/builder/builder.go | 25 +++++++++++ pkg/{feed => builder}/url.go | 2 +- pkg/{feed => builder}/url_test.go | 2 +- pkg/{feed => builder}/vimeo.go | 2 +- pkg/{feed => builder}/vimeo_test.go | 2 +- pkg/{feed => builder}/youtube.go | 2 +- pkg/{feed => builder}/youtube_test.go | 2 +- pkg/feed/common.go | 43 ------------------- pkg/feed/deps.go | 17 ++++++++ ...{common_mock_test.go => deps_mock_test.go} | 41 +----------------- pkg/feed/opml_test.go | 2 - 12 files changed, 61 insertions(+), 92 deletions(-) create mode 100644 pkg/builder/builder.go rename pkg/{feed => builder}/url.go (99%) rename pkg/{feed => builder}/url_test.go (99%) rename pkg/{feed => builder}/vimeo.go (99%) rename pkg/{feed => builder}/vimeo_test.go (99%) rename pkg/{feed => builder}/youtube.go (99%) rename pkg/{feed => builder}/youtube_test.go (99%) delete mode 100644 pkg/feed/common.go create mode 100644 pkg/feed/deps.go rename pkg/feed/{common_mock_test.go => deps_mock_test.go} (69%) diff --git a/cmd/podsync/updater.go b/cmd/podsync/updater.go index 37d45409..f1487617 100644 --- a/cmd/podsync/updater.go +++ b/cmd/podsync/updater.go @@ -14,6 +14,7 @@ import ( "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "github.com/mxpv/podsync/pkg/builder" "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/db" "github.com/mxpv/podsync/pkg/feed" @@ -90,8 +91,18 @@ func (u *Updater) Update(ctx context.Context, feedConfig *config.Feed) error { // updateFeed pulls API for new episodes and saves them to database func (u *Updater) updateFeed(ctx context.Context, feedConfig *config.Feed) error { + info, err := builder.ParseURL(feedConfig.URL) + if err != nil { + return errors.Wrapf(err, "failed to parse URL: %s", feedConfig.URL) + } + + keyProvider, ok := u.keys[info.Provider] + if !ok { + return errors.Errorf("key provider %q not loaded", info.Provider) + } + // Create an updater for this feed type - provider, err := feed.New(ctx, feedConfig, u.keys) + provider, err := builder.New(ctx, info.Provider, keyProvider.Get()) if err != nil { return err } diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go new file mode 100644 index 00000000..7687cd05 --- /dev/null +++ b/pkg/builder/builder.go @@ -0,0 +1,25 @@ +package builder + +import ( + "context" + + "github.com/pkg/errors" + + "github.com/mxpv/podsync/pkg/config" + "github.com/mxpv/podsync/pkg/model" +) + +type Builder interface { + Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) +} + +func New(ctx context.Context, provider model.Provider, key string) (Builder, error) { + switch provider { + case model.ProviderYoutube: + return NewYouTubeBuilder(key) + case model.ProviderVimeo: + return NewVimeoBuilder(ctx, key) + default: + return nil, errors.Errorf("unsupported provider %q", provider) + } +} diff --git a/pkg/feed/url.go b/pkg/builder/url.go similarity index 99% rename from pkg/feed/url.go rename to pkg/builder/url.go index 5566f198..7fef9198 100644 --- a/pkg/feed/url.go +++ b/pkg/builder/url.go @@ -1,4 +1,4 @@ -package feed +package builder import ( "net/url" diff --git a/pkg/feed/url_test.go b/pkg/builder/url_test.go similarity index 99% rename from pkg/feed/url_test.go rename to pkg/builder/url_test.go index 5b09a801..48625b52 100644 --- a/pkg/feed/url_test.go +++ b/pkg/builder/url_test.go @@ -1,4 +1,4 @@ -package feed +package builder import ( "net/url" diff --git a/pkg/feed/vimeo.go b/pkg/builder/vimeo.go similarity index 99% rename from pkg/feed/vimeo.go rename to pkg/builder/vimeo.go index 758debc6..a97747c2 100644 --- a/pkg/feed/vimeo.go +++ b/pkg/builder/vimeo.go @@ -1,4 +1,4 @@ -package feed +package builder import ( "net/http" diff --git a/pkg/feed/vimeo_test.go b/pkg/builder/vimeo_test.go similarity index 99% rename from pkg/feed/vimeo_test.go rename to pkg/builder/vimeo_test.go index b43daebd..dba71e32 100644 --- a/pkg/feed/vimeo_test.go +++ b/pkg/builder/vimeo_test.go @@ -1,4 +1,4 @@ -package feed +package builder import ( "context" diff --git a/pkg/feed/youtube.go b/pkg/builder/youtube.go similarity index 99% rename from pkg/feed/youtube.go rename to pkg/builder/youtube.go index b0d400f6..ec6dbc24 100644 --- a/pkg/feed/youtube.go +++ b/pkg/builder/youtube.go @@ -1,4 +1,4 @@ -package feed +package builder import ( "context" diff --git a/pkg/feed/youtube_test.go b/pkg/builder/youtube_test.go similarity index 99% rename from pkg/feed/youtube_test.go rename to pkg/builder/youtube_test.go index 2c2483c3..dfbc31a2 100644 --- a/pkg/feed/youtube_test.go +++ b/pkg/builder/youtube_test.go @@ -1,4 +1,4 @@ -package feed +package builder import ( "context" diff --git a/pkg/feed/common.go b/pkg/feed/common.go deleted file mode 100644 index 5f75def3..00000000 --- a/pkg/feed/common.go +++ /dev/null @@ -1,43 +0,0 @@ -package feed - -import ( - "context" - - "github.com/pkg/errors" - - "github.com/mxpv/podsync/pkg/config" - "github.com/mxpv/podsync/pkg/model" -) - -type Builder interface { - Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) -} - -func New(ctx context.Context, cfg *config.Feed, keys map[model.Provider]KeyProvider) (Builder, error) { - info, err := ParseURL(cfg.URL) - if err != nil { - return nil, err - } - - keyProvider, ok := keys[info.Provider] - if !ok { - return nil, errors.Errorf("unknown key provider: %s", info.Provider) - } - - switch info.Provider { - case model.ProviderYoutube: - return NewYouTubeBuilder(keyProvider.Get()) - case model.ProviderVimeo: - return NewVimeoBuilder(ctx, keyProvider.Get()) - default: - return nil, errors.Errorf("unsupported provider %q", info.Provider) - } -} - -type feedProvider interface { - GetFeed(ctx context.Context, feedID string) (*model.Feed, error) -} - -type urlProvider interface { - URL(ctx context.Context, ns string, fileName string) (string, error) -} diff --git a/pkg/feed/deps.go b/pkg/feed/deps.go new file mode 100644 index 00000000..4c568aa9 --- /dev/null +++ b/pkg/feed/deps.go @@ -0,0 +1,17 @@ +//go:generate mockgen -source=deps.go -destination=deps_mock_test.go -package=feed + +package feed + +import ( + "context" + + "github.com/mxpv/podsync/pkg/model" +) + +type feedProvider interface { + GetFeed(ctx context.Context, feedID string) (*model.Feed, error) +} + +type urlProvider interface { + URL(ctx context.Context, ns string, fileName string) (string, error) +} diff --git a/pkg/feed/common_mock_test.go b/pkg/feed/deps_mock_test.go similarity index 69% rename from pkg/feed/common_mock_test.go rename to pkg/feed/deps_mock_test.go index 7954e90f..e7dec7d1 100644 --- a/pkg/feed/common_mock_test.go +++ b/pkg/feed/deps_mock_test.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: common.go +// Source: deps.go // Package feed is a generated GoMock package. package feed @@ -7,49 +7,10 @@ package feed import ( context "context" gomock "github.com/golang/mock/gomock" - config "github.com/mxpv/podsync/pkg/config" model "github.com/mxpv/podsync/pkg/model" reflect "reflect" ) -// MockBuilder is a mock of Builder interface -type MockBuilder struct { - ctrl *gomock.Controller - recorder *MockBuilderMockRecorder -} - -// MockBuilderMockRecorder is the mock recorder for MockBuilder -type MockBuilderMockRecorder struct { - mock *MockBuilder -} - -// NewMockBuilder creates a new mock instance -func NewMockBuilder(ctrl *gomock.Controller) *MockBuilder { - mock := &MockBuilder{ctrl: ctrl} - mock.recorder = &MockBuilderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockBuilder) EXPECT() *MockBuilderMockRecorder { - return m.recorder -} - -// Build mocks base method -func (m *MockBuilder) Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Build", ctx, cfg) - ret0, _ := ret[0].(*model.Feed) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Build indicates an expected call of Build -func (mr *MockBuilderMockRecorder) Build(ctx, cfg interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Build", reflect.TypeOf((*MockBuilder)(nil).Build), ctx, cfg) -} - // MockfeedProvider is a mock of feedProvider interface type MockfeedProvider struct { ctrl *gomock.Controller diff --git a/pkg/feed/opml_test.go b/pkg/feed/opml_test.go index 3aa92a81..1697676c 100644 --- a/pkg/feed/opml_test.go +++ b/pkg/feed/opml_test.go @@ -1,5 +1,3 @@ -//go:generate mockgen -source=common.go -destination=common_mock_test.go -package=feed - package feed import ( From 28429607cd82bc12681f7dd37ee738fba2b43825 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Mon, 20 Apr 2020 21:06:51 -0700 Subject: [PATCH 6/6] Update README --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80a4ae77..4e0d249d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ any device in podcast client. - Runs on Windows, Mac OS, Linux, and Docker. - Supports ARM. - Automatic youtube-dl self update. +- Supports API keys rotation. ## Dependencies @@ -58,9 +59,13 @@ Here is an example how configuration might look like: port = 8080 data_dir = "/app/data" # Don't change if you run podsync via docker +# Tokens from `Access tokens` section [tokens] -youtube = "{YOUTUBE_API_TOKEN}" # Tokens from `Access tokens` section -vimeo = "{VIMEO_API_TOKEN}" +youtube = "YOUTUBE_API_TOKEN" # YouTube API Key. See https://developers.google.com/youtube/registering_an_application +vimeo = [ # Multiple keys will be rotated. + "VIMEO_API_KEY_1", # Vimeo developer keys. See https://developer.vimeo.com/api/guides/start#generate-access-token + "VIMEO_API_KEY_2" +] [feeds] [feeds.ID1]