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) }