diff --git a/pkg/config/config.go b/cmd/podsync/config.go similarity index 54% rename from pkg/config/config.go rename to cmd/podsync/config.go index cd1a4a40..bedffd55 100644 --- a/pkg/config/config.go +++ b/cmd/podsync/config.go @@ -1,78 +1,22 @@ -package config +package main import ( "fmt" "io/ioutil" "path/filepath" "regexp" - "time" "github.com/hashicorp/go-multierror" "github.com/pelletier/go-toml" "github.com/pkg/errors" + "github.com/mxpv/podsync/pkg/db" + "github.com/mxpv/podsync/pkg/feed" "github.com/mxpv/podsync/pkg/model" + "github.com/mxpv/podsync/pkg/ytdl" ) -// Feed is a configuration for a feed -type Feed struct { - ID string `toml:"-"` - // URL is a full URL of the field - URL string `toml:"url"` - // PageSize is the number of pages to query from YouTube API. - // NOTE: larger page sizes/often requests might drain your API token. - PageSize int `toml:"page_size"` - // UpdatePeriod is how often to check for updates. - // Format is "300ms", "1.5h" or "2h45m". - // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". - // NOTE: too often update check might drain your API token. - UpdatePeriod time.Duration `toml:"update_period"` - // Cron expression format is how often to check update - // NOTE: too often update check might drain your API token. - CronSchedule string `toml:"cron_schedule"` - // Quality to use for this feed - Quality model.Quality `toml:"quality"` - // Maximum height of video - MaxHeight int `toml:"max_height"` - // Format to use for this feed - Format model.Format `toml:"format"` - // Only download episodes that match this regexp (defaults to matching anything) - Filters Filters `toml:"filters"` - // Clean is a cleanup policy to use for this feed - Clean Cleanup `toml:"clean"` - // Custom is a list of feed customizations - Custom Custom `toml:"custom"` - // List of additional youtube-dl arguments passed at download time - YouTubeDLArgs []string `toml:"youtube_dl_args"` - // Included in OPML file - OPML bool `toml:"opml"` - // Playlist sort - PlaylistSort model.Sorting `toml:"playlist_sort"` -} - -type Filters struct { - Title string `toml:"title"` - NotTitle string `toml:"not_title"` - Description string `toml:"description"` - NotDescription string `toml:"not_description"` - // More filters to be added here -} - -type Custom struct { - CoverArt string `toml:"cover_art"` - CoverArtQuality model.Quality `toml:"cover_art_quality"` - Category string `toml:"category"` - Subcategories []string `toml:"subcategories"` - Explicit bool `toml:"explicit"` - Language string `toml:"lang"` - Author string `toml:"author"` - Title string `toml:"title"` - Description string `toml:"description"` - OwnerName string `toml:"ownerName"` - OwnerEmail string `toml:"ownerEmail"` -} - -type Server struct { +type ServerConfig struct { // Hostname to use for download links Hostname string `toml:"hostname"` // Port is a server port to listen to @@ -88,24 +32,6 @@ type Server struct { DataDir string `toml:"data_dir"` } -type Database struct { - // Dir is a directory to keep database files - Dir string `toml:"dir"` - Badger *Badger `toml:"badger"` -} - -// Badger represents BadgerDB configuration parameters -// See https://github.com/dgraph-io/badger#memory-usage -type Badger struct { - Truncate bool `toml:"truncate"` - FileIO bool `toml:"file_io"` -} - -type Cleanup struct { - // KeepLast defines how many episodes to keep - KeepLast int `toml:"keep_last"` -} - type Log struct { // Filename to write the log to (instead of stdout) Filename string `toml:"filename"` @@ -119,28 +45,20 @@ type Log struct { Compress bool `toml:"compress"` } -// Downloader is a youtube-dl related configuration -type Downloader struct { - // SelfUpdate toggles self update every 24 hour - SelfUpdate bool `toml:"self_update"` - // Timeout in minutes for youtube-dl process to finish download - Timeout int `toml:"timeout"` -} - type Config struct { // Server is the web server configuration - Server Server `toml:"server"` + Server ServerConfig `toml:"server"` // Log is the optional logging configuration Log Log `toml:"log"` // Database configuration - Database Database `toml:"database"` + Database db.Config `toml:"database"` // Feeds is a list of feeds to host by this app. // ID will be used as feed ID in http://podsync.net/{FEED_ID}.xml - Feeds map[string]*Feed + Feeds map[string]*feed.Config // Tokens is API keys to use to access YouTube/Vimeo APIs. Tokens map[model.Provider][]string `toml:"tokens"` // Downloader (youtube-dl) configuration - Downloader Downloader `toml:"downloader"` + Downloader ytdl.Config `toml:"downloader"` } // LoadConfig loads TOML configuration from a file path @@ -155,8 +73,8 @@ func LoadConfig(path string) (*Config, error) { return nil, errors.Wrap(err, "failed to unmarshal toml") } - for id, feed := range config.Feeds { - feed.ID = id + for id, f := range config.Feeds { + f.ID = id } config.applyDefaults(path) @@ -186,8 +104,8 @@ func (c *Config) validate() error { result = multierror.Append(result, errors.New("at least one feed must be specified")) } - for id, feed := range c.Feeds { - if feed.URL == "" { + for id, f := range c.Feeds { + if f.URL == "" { result = multierror.Append(result, errors.Errorf("URL is required for %q", id)) } } diff --git a/pkg/config/config_test.go b/cmd/podsync/config_test.go similarity index 99% rename from pkg/config/config_test.go rename to cmd/podsync/config_test.go index 2b3e1c7f..c078cee2 100644 --- a/pkg/config/config_test.go +++ b/cmd/podsync/config_test.go @@ -1,4 +1,4 @@ -package config +package main import ( "io/ioutil" @@ -173,7 +173,7 @@ data_dir = "/data" func TestDefaultHostname(t *testing.T) { cfg := Config{ - Server: Server{}, + Server: ServerConfig{}, } t.Run("empty hostname", func(t *testing.T) { diff --git a/cmd/podsync/main.go b/cmd/podsync/main.go index 56452995..7d048679 100644 --- a/cmd/podsync/main.go +++ b/cmd/podsync/main.go @@ -10,12 +10,12 @@ import ( "time" "github.com/jessevdk/go-flags" + "github.com/mxpv/podsync/pkg/feed" "github.com/robfig/cron/v3" log "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" "gopkg.in/natefinch/lumberjack.v2" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/db" "github.com/mxpv/podsync/pkg/fs" "github.com/mxpv/podsync/pkg/ytdl" @@ -75,7 +75,7 @@ func main() { // Load TOML file log.Debugf("loading configuration %q", opts.ConfigPath) - cfg, err := config.LoadConfig(opts.ConfigPath) + cfg, err := LoadConfig(opts.ConfigPath) if err != nil { log.WithError(err).Fatal("failed to load configuration file") } @@ -121,7 +121,7 @@ func main() { } // Queue of feeds to update - updates := make(chan *config.Feed, 16) + updates := make(chan *feed.Config, 16) defer close(updates) // Create Cron diff --git a/cmd/podsync/server.go b/cmd/podsync/server.go index 2e49090a..809e871f 100644 --- a/cmd/podsync/server.go +++ b/cmd/podsync/server.go @@ -5,15 +5,13 @@ import ( "net/http" log "github.com/sirupsen/logrus" - - "github.com/mxpv/podsync/pkg/config" ) type Server struct { http.Server } -func NewServer(cfg *config.Config, storage http.FileSystem) *Server { +func NewServer(cfg *Config, storage http.FileSystem) *Server { port := cfg.Server.Port if port == 0 { port = 8080 diff --git a/cmd/podsync/updater.go b/cmd/podsync/updater.go index 54bdf9ce..35c07447 100644 --- a/cmd/podsync/updater.go +++ b/cmd/podsync/updater.go @@ -15,7 +15,6 @@ import ( 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" "github.com/mxpv/podsync/pkg/fs" @@ -24,18 +23,18 @@ import ( ) type Downloader interface { - Download(ctx context.Context, feedConfig *config.Feed, episode *model.Episode) (io.ReadCloser, error) + Download(ctx context.Context, feedConfig *feed.Config, episode *model.Episode) (io.ReadCloser, error) } type Updater struct { - config *config.Config + config *Config 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) { +func NewUpdater(config *Config, downloader Downloader, db db.Storage, fs fs.Storage) (*Updater, error) { keys := map[model.Provider]feed.KeyProvider{} for name, list := range config.Tokens { @@ -55,7 +54,7 @@ func NewUpdater(config *config.Config, downloader Downloader, db db.Storage, fs }, nil } -func (u *Updater) Update(ctx context.Context, feedConfig *config.Feed) error { +func (u *Updater) Update(ctx context.Context, feedConfig *feed.Config) error { log.WithFields(log.Fields{ "feed_id": feedConfig.ID, "format": feedConfig.Format, @@ -90,7 +89,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 { +func (u *Updater) updateFeed(ctx context.Context, feedConfig *feed.Config) error { info, err := builder.ParseURL(feedConfig.URL) if err != nil { return errors.Wrapf(err, "failed to parse URL: %s", feedConfig.URL) @@ -162,7 +161,7 @@ func (u *Updater) matchRegexpFilter(pattern, str string, negative bool, logger l return true } -func (u *Updater) matchFilters(episode *model.Episode, filters *config.Filters) bool { +func (u *Updater) matchFilters(episode *model.Episode, filters *feed.Filters) bool { logger := log.WithFields(log.Fields{"episode_id": episode.ID}) if !u.matchRegexpFilter(filters.Title, episode.Title, false, logger.WithField("filter", "title")) { return false @@ -181,7 +180,7 @@ func (u *Updater) matchFilters(episode *model.Episode, filters *config.Filters) return true } -func (u *Updater) downloadEpisodes(ctx context.Context, feedConfig *config.Feed) error { +func (u *Updater) downloadEpisodes(ctx context.Context, feedConfig *feed.Config) error { var ( feedID = feedConfig.ID downloadList []*model.Episode @@ -308,7 +307,7 @@ func (u *Updater) downloadEpisodes(ctx context.Context, feedConfig *config.Feed) return nil } -func (u *Updater) buildXML(ctx context.Context, feedConfig *config.Feed) error { +func (u *Updater) buildXML(ctx context.Context, feedConfig *feed.Config) error { f, err := u.db.GetFeed(ctx, feedConfig.ID) if err != nil { return err @@ -336,7 +335,7 @@ func (u *Updater) buildXML(ctx context.Context, feedConfig *config.Feed) error { func (u *Updater) buildOPML(ctx context.Context) error { // Build OPML with data received from builder log.Debug("building podcast OPML") - opml, err := feed.BuildOPML(ctx, u.config, u.db, u.config.Server.Hostname) + opml, err := feed.BuildOPML(ctx, u.config.Feeds, u.db, u.config.Server.Hostname) if err != nil { return err } @@ -353,7 +352,7 @@ func (u *Updater) buildOPML(ctx context.Context) error { return nil } -func (u *Updater) cleanup(ctx context.Context, feedConfig *config.Feed) error { +func (u *Updater) cleanup(ctx context.Context, feedConfig *feed.Config) error { var ( feedID = feedConfig.ID logger = log.WithField("feed_id", feedID) diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index 8ad6ead7..42b6d0a1 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -3,14 +3,14 @@ package builder import ( "context" + "github.com/mxpv/podsync/pkg/feed" "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) + Build(ctx context.Context, cfg *feed.Config) (*model.Feed, error) } func New(ctx context.Context, provider model.Provider, key string) (Builder, error) { diff --git a/pkg/builder/soundcloud.go b/pkg/builder/soundcloud.go index 5e569d98..8a935060 100644 --- a/pkg/builder/soundcloud.go +++ b/pkg/builder/soundcloud.go @@ -5,10 +5,10 @@ import ( "strconv" "time" + "github.com/mxpv/podsync/pkg/feed" "github.com/pkg/errors" soundcloudapi "github.com/zackradisic/soundcloud-api" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -16,7 +16,7 @@ type SoundCloudBuilder struct { client *soundcloudapi.API } -func (s *SoundCloudBuilder) Build(_ctx context.Context, cfg *config.Feed) (*model.Feed, error) { +func (s *SoundCloudBuilder) Build(_ctx context.Context, cfg *feed.Config) (*model.Feed, error) { info, err := ParseURL(cfg.URL) if err != nil { return nil, err diff --git a/pkg/builder/soundcloud_test.go b/pkg/builder/soundcloud_test.go index e980ec4b..6b64ca48 100644 --- a/pkg/builder/soundcloud_test.go +++ b/pkg/builder/soundcloud_test.go @@ -3,10 +3,9 @@ package builder import ( "testing" + "github.com/mxpv/podsync/pkg/feed" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/mxpv/podsync/pkg/config" ) func TestSC_BUILDFEED(t *testing.T) { @@ -20,7 +19,7 @@ func TestSC_BUILDFEED(t *testing.T) { for _, addr := range urls { t.Run(addr, func(t *testing.T) { - feed, err := builder.Build(testCtx, &config.Feed{URL: addr}) + feed, err := builder.Build(testCtx, &feed.Config{URL: addr}) require.NoError(t, err) assert.NotEmpty(t, feed.Title) diff --git a/pkg/builder/vimeo.go b/pkg/builder/vimeo.go index 6429fff2..72e10eee 100644 --- a/pkg/builder/vimeo.go +++ b/pkg/builder/vimeo.go @@ -6,11 +6,11 @@ import ( "strconv" "time" + "github.com/mxpv/podsync/pkg/feed" "github.com/pkg/errors" "github.com/silentsokolov/go-vimeo/vimeo" "golang.org/x/oauth2" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -158,7 +158,7 @@ func (v *VimeoBuilder) queryVideos(getVideos getVideosFunc, feed *model.Feed) er } } -func (v *VimeoBuilder) Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) { +func (v *VimeoBuilder) Build(ctx context.Context, cfg *feed.Config) (*model.Feed, error) { info, err := ParseURL(cfg.URL) if err != nil { return nil, err diff --git a/pkg/builder/youtube.go b/pkg/builder/youtube.go index a0a00409..885e034b 100644 --- a/pkg/builder/youtube.go +++ b/pkg/builder/youtube.go @@ -10,10 +10,10 @@ import ( "time" "github.com/BrianHicks/finch/duration" + "github.com/mxpv/podsync/pkg/feed" "github.com/pkg/errors" "google.golang.org/api/youtube/v3" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -384,7 +384,7 @@ func (yt *YouTubeBuilder) queryItems(ctx context.Context, feed *model.Feed) erro return nil } -func (yt *YouTubeBuilder) Build(ctx context.Context, cfg *config.Feed) (*model.Feed, error) { +func (yt *YouTubeBuilder) Build(ctx context.Context, cfg *feed.Config) (*model.Feed, error) { info, err := ParseURL(cfg.URL) if err != nil { return nil, err diff --git a/pkg/builder/youtube_test.go b/pkg/builder/youtube_test.go index dfbc31a2..39259b54 100644 --- a/pkg/builder/youtube_test.go +++ b/pkg/builder/youtube_test.go @@ -5,10 +5,10 @@ import ( "os" "testing" + "github.com/mxpv/podsync/pkg/feed" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -53,7 +53,7 @@ func TestYT_BuildFeed(t *testing.T) { for _, addr := range urls { t.Run(addr, func(t *testing.T) { - feed, err := builder.Build(testCtx, &config.Feed{URL: addr}) + feed, err := builder.Build(testCtx, &feed.Config{URL: addr}) require.NoError(t, err) assert.NotEmpty(t, feed.Title) diff --git a/pkg/db/badger.go b/pkg/db/badger.go index 41e6ad5b..fd8ef7d0 100644 --- a/pkg/db/badger.go +++ b/pkg/db/badger.go @@ -11,7 +11,6 @@ import ( "github.com/pkg/errors" log "github.com/sirupsen/logrus" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -23,13 +22,20 @@ const ( episodePath = "episode/%s/%s" // FeedID + EpisodeID ) +// BadgerConfig represents BadgerDB configuration parameters +// See https://github.com/dgraph-io/badger#memory-usage +type BadgerConfig struct { + Truncate bool `toml:"truncate"` + FileIO bool `toml:"file_io"` +} + type Badger struct { db *badger.DB } var _ Storage = (*Badger)(nil) -func NewBadger(config *config.Database) (*Badger, error) { +func NewBadger(config *Config) (*Badger, error) { var ( dir = config.Dir ) diff --git a/pkg/db/badger_test.go b/pkg/db/badger_test.go index 1b0b99b2..a188493f 100644 --- a/pkg/db/badger_test.go +++ b/pkg/db/badger_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -20,7 +19,7 @@ func TestNewBadger(t *testing.T) { dir, err := ioutil.TempDir("", "podsync-badger-") require.NoError(t, err) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) err = db.Close() @@ -35,7 +34,7 @@ func TestBadger_Version(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(dir) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) defer db.Close() @@ -49,7 +48,7 @@ func TestBadger_AddFeed(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(dir) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) defer db.Close() @@ -63,7 +62,7 @@ func TestBadger_GetFeed(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(dir) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) defer db.Close() @@ -83,7 +82,7 @@ func TestBadger_WalkFeeds(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(dir) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) defer db.Close() @@ -109,7 +108,7 @@ func TestBadger_DeleteFeed(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(dir) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) defer db.Close() @@ -134,7 +133,7 @@ func TestBadger_UpdateEpisode(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(dir) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) defer db.Close() @@ -164,7 +163,7 @@ func TestBadger_WalkEpisodes(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(dir) - db, err := NewBadger(&config.Database{Dir: dir}) + db, err := NewBadger(&Config{Dir: dir}) require.NoError(t, err) defer db.Close() diff --git a/pkg/db/config.go b/pkg/db/config.go new file mode 100644 index 00000000..cd5c1f9a --- /dev/null +++ b/pkg/db/config.go @@ -0,0 +1,7 @@ +package db + +type Config struct { + // Dir is a directory to keep database files + Dir string `toml:"dir"` + Badger *BadgerConfig `toml:"badger"` +} diff --git a/pkg/feed/config.go b/pkg/feed/config.go new file mode 100644 index 00000000..49bc5b20 --- /dev/null +++ b/pkg/feed/config.go @@ -0,0 +1,70 @@ +package feed + +import ( + "time" + + "github.com/mxpv/podsync/pkg/model" +) + +// Config is a configuration for a feed loaded from TOML +type Config struct { + ID string `toml:"-"` + // URL is a full URL of the field + URL string `toml:"url"` + // PageSize is the number of pages to query from YouTube API. + // NOTE: larger page sizes/often requests might drain your API token. + PageSize int `toml:"page_size"` + // UpdatePeriod is how often to check for updates. + // Format is "300ms", "1.5h" or "2h45m". + // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + // NOTE: too often update check might drain your API token. + UpdatePeriod time.Duration `toml:"update_period"` + // Cron expression format is how often to check update + // NOTE: too often update check might drain your API token. + CronSchedule string `toml:"cron_schedule"` + // Quality to use for this feed + Quality model.Quality `toml:"quality"` + // Maximum height of video + MaxHeight int `toml:"max_height"` + // Format to use for this feed + Format model.Format `toml:"format"` + // Only download episodes that match this regexp (defaults to matching anything) + Filters Filters `toml:"filters"` + // Clean is a cleanup policy to use for this feed + Clean Cleanup `toml:"clean"` + // Custom is a list of feed customizations + Custom Custom `toml:"custom"` + // List of additional youtube-dl arguments passed at download time + YouTubeDLArgs []string `toml:"youtube_dl_args"` + // Included in OPML file + OPML bool `toml:"opml"` + // Playlist sort + PlaylistSort model.Sorting `toml:"playlist_sort"` +} + +type Filters struct { + Title string `toml:"title"` + NotTitle string `toml:"not_title"` + Description string `toml:"description"` + NotDescription string `toml:"not_description"` + // More filters to be added here +} + +type Custom struct { + CoverArt string `toml:"cover_art"` + CoverArtQuality model.Quality `toml:"cover_art_quality"` + Category string `toml:"category"` + Subcategories []string `toml:"subcategories"` + Explicit bool `toml:"explicit"` + Language string `toml:"lang"` + Author string `toml:"author"` + Title string `toml:"title"` + Description string `toml:"description"` + OwnerName string `toml:"ownerName"` + OwnerEmail string `toml:"ownerEmail"` +} + +type Cleanup struct { + // KeepLast defines how many episodes to keep + KeepLast int `toml:"keep_last"` +} diff --git a/pkg/feed/opml.go b/pkg/feed/opml.go index 597b5e47..5fb393b4 100644 --- a/pkg/feed/opml.go +++ b/pkg/feed/opml.go @@ -9,16 +9,15 @@ import ( "github.com/pkg/errors" log "github.com/sirupsen/logrus" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) -func BuildOPML(ctx context.Context, config *config.Config, db feedProvider, hostname string) (string, error) { +func BuildOPML(ctx context.Context, feeds map[string]*Config, db feedProvider, hostname string) (string, error) { doc := opml.OPML{Version: "1.0"} doc.Head = opml.Head{Title: "Podsync feeds"} doc.Body = opml.Body{} - for _, feed := range config.Feeds { + for _, feed := range feeds { f, err := db.GetFeed(ctx, feed.ID) if err == model.ErrNotFound { // As we update OPML on per-feed basis, some feeds may not yet be populated in database. diff --git a/pkg/feed/opml_test.go b/pkg/feed/opml_test.go index d5d4ab7d..467586fa 100644 --- a/pkg/feed/opml_test.go +++ b/pkg/feed/opml_test.go @@ -7,7 +7,6 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -28,13 +27,8 @@ func TestBuildOPML(t *testing.T) { dbMock := NewMockfeedProvider(ctrl) dbMock.EXPECT().GetFeed(gomock.Any(), "1").Return(&model.Feed{Title: "1", Description: "desc"}, nil) - cfg := config.Config{ - Feeds: map[string]*config.Feed{ - "any": {ID: "1", OPML: true}, - }, - } - - out, err := BuildOPML(context.Background(), &cfg, dbMock, "https://url/") + feeds := map[string]*Config{"any": {ID: "1", OPML: true}} + out, err := BuildOPML(context.Background(), feeds, dbMock, "https://url/") assert.NoError(t, err) assert.Equal(t, expected, out) } diff --git a/pkg/feed/xml.go b/pkg/feed/xml.go index ac05312f..7d5d959e 100644 --- a/pkg/feed/xml.go +++ b/pkg/feed/xml.go @@ -11,7 +11,6 @@ import ( itunes "github.com/eduncan911/podcast" "github.com/pkg/errors" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -31,7 +30,7 @@ func (p timeSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -func Build(_ctx context.Context, feed *model.Feed, cfg *config.Feed, hostname string) (*itunes.Podcast, error) { +func Build(_ctx context.Context, feed *model.Feed, cfg *Config, hostname string) (*itunes.Podcast, error) { const ( podsyncGenerator = "Podsync generator (support us at https://github.com/mxpv/podsync)" defaultCategory = "TV & Film" @@ -152,7 +151,7 @@ func Build(_ctx context.Context, feed *model.Feed, cfg *config.Feed, hostname st return &p, nil } -func EpisodeName(feedConfig *config.Feed, episode *model.Episode) string { +func EpisodeName(feedConfig *Config, episode *model.Episode) string { ext := "mp4" if feedConfig.Format == model.FormatAudio { ext = "mp3" diff --git a/pkg/feed/xml_test.go b/pkg/feed/xml_test.go index eba47237..00a803dc 100644 --- a/pkg/feed/xml_test.go +++ b/pkg/feed/xml_test.go @@ -5,7 +5,6 @@ import ( "testing" itunes "github.com/eduncan911/podcast" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -23,9 +22,9 @@ func TestBuildXML(t *testing.T) { }, } - cfg := config.Feed{ + cfg := Config{ ID: "test", - Custom: config.Custom{Description: "description", Category: "Technology", Subcategories: []string{"Gadgets", "Podcasting"}}, + Custom: Custom{Description: "description", Category: "Technology", Subcategories: []string{"Gadgets", "Podcasting"}}, } out, err := Build(context.Background(), &feed, &cfg, "http://localhost/") diff --git a/pkg/ytdl/ytdl.go b/pkg/ytdl/ytdl.go index fe87752f..7b9529be 100644 --- a/pkg/ytdl/ytdl.go +++ b/pkg/ytdl/ytdl.go @@ -13,10 +13,10 @@ import ( "sync" "time" + "github.com/mxpv/podsync/pkg/feed" "github.com/pkg/errors" log "github.com/sirupsen/logrus" - "github.com/mxpv/podsync/pkg/config" "github.com/mxpv/podsync/pkg/model" ) @@ -29,13 +29,21 @@ var ( ErrTooManyRequests = errors.New(http.StatusText(http.StatusTooManyRequests)) ) +// Config is a youtube-dl related configuration +type Config struct { + // SelfUpdate toggles self update every 24 hour + SelfUpdate bool `toml:"self_update"` + // Timeout in minutes for youtube-dl process to finish download + Timeout int `toml:"timeout"` +} + type YoutubeDl struct { path string timeout time.Duration updateLock sync.Mutex // Don't call youtube-dl while self updating } -func New(ctx context.Context, cfg config.Downloader) (*YoutubeDl, error) { +func New(ctx context.Context, cfg Config) (*YoutubeDl, error) { path, err := exec.LookPath("youtube-dl") if err != nil { return nil, errors.Wrap(err, "youtube-dl binary not found") @@ -134,7 +142,7 @@ func (dl *YoutubeDl) Update(ctx context.Context) error { return nil } -func (dl *YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episode *model.Episode) (r io.ReadCloser, err error) { +func (dl *YoutubeDl) Download(ctx context.Context, feedConfig *feed.Config, episode *model.Episode) (r io.ReadCloser, err error) { tmpDir, err := ioutil.TempDir("", "podsync-") if err != nil { return nil, errors.Wrap(err, "failed to get temp dir for download") @@ -198,7 +206,7 @@ func (dl *YoutubeDl) exec(ctx context.Context, args ...string) (string, error) { return string(output), nil } -func buildArgs(feedConfig *config.Feed, episode *model.Episode, outputFilePath string) []string { +func buildArgs(feedConfig *feed.Config, episode *model.Episode, outputFilePath string) []string { var args []string if feedConfig.Format == model.FormatVideo { diff --git a/pkg/ytdl/ytdl_test.go b/pkg/ytdl/ytdl_test.go index 7ea1a965..c55571ef 100644 --- a/pkg/ytdl/ytdl_test.go +++ b/pkg/ytdl/ytdl_test.go @@ -3,7 +3,7 @@ package ytdl import ( "testing" - "github.com/mxpv/podsync/pkg/config" + "github.com/mxpv/podsync/pkg/feed" "github.com/mxpv/podsync/pkg/model" "github.com/stretchr/testify/assert" @@ -105,7 +105,7 @@ func TestBuildArgs(t *testing.T) { for _, tst := range tests { t.Run(tst.name, func(t *testing.T) { - result := buildArgs(&config.Feed{ + result := buildArgs(&feed.Config{ Format: tst.format, Quality: tst.quality, MaxHeight: tst.maxHeight,