From e0290afa1155328b230745df65ee781cf87bedac Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Sat, 7 Mar 2020 17:25:55 -0800 Subject: [PATCH] Refactor updater --- cmd/podsync/updater.go | 128 ++--------------------------------------- pkg/feed/build.go | 111 +++++++++++++++++++++++++++++++++++ pkg/feed/common.go | 27 ++++++++- 3 files changed, 142 insertions(+), 124 deletions(-) create mode 100644 pkg/feed/build.go diff --git a/cmd/podsync/updater.go b/cmd/podsync/updater.go index f3af013..44d0fe7 100644 --- a/cmd/podsync/updater.go +++ b/cmd/podsync/updater.go @@ -7,10 +7,8 @@ import ( "io" "os" "regexp" - "strconv" "time" - itunes "github.com/eduncan911/podcast" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -18,7 +16,6 @@ import ( "github.com/mxpv/podsync/pkg/db" "github.com/mxpv/podsync/pkg/feed" "github.com/mxpv/podsync/pkg/fs" - "github.com/mxpv/podsync/pkg/link" "github.com/mxpv/podsync/pkg/model" "github.com/mxpv/podsync/pkg/ytdl" ) @@ -49,6 +46,7 @@ func (u *Updater) Update(ctx context.Context, feedConfig *config.Feed) error { "format": feedConfig.Format, "quality": feedConfig.Quality, }).Infof("-> updating %s", feedConfig.URL) + started := time.Now() if err := u.updateFeed(ctx, feedConfig); err != nil { @@ -72,7 +70,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 := u.makeBuilder(ctx, feedConfig) + provider, err := feed.New(ctx, feedConfig, u.config.Tokens) if err != nil { return err } @@ -142,7 +140,7 @@ func (u *Updater) downloadEpisodes(ctx context.Context, feedConfig *config.Feed) for idx, episode := range downloadList { var ( logger = log.WithFields(log.Fields{"index": idx, "episode_id": episode.ID}) - episodeName = u.episodeName(feedConfig, episode) + episodeName = feed.EpisodeName(feedConfig, episode) ) // Check whether episode already exists @@ -220,14 +218,14 @@ func (u *Updater) downloadEpisodes(ctx context.Context, feedConfig *config.Feed) } func (u *Updater) buildXML(ctx context.Context, feedConfig *config.Feed) error { - feed, err := u.db.GetFeed(ctx, feedConfig.ID) + f, err := u.db.GetFeed(ctx, feedConfig.ID) if err != nil { return err } // Build iTunes XML feed with data received from builder log.Debug("building iTunes podcast feed") - podcast, err := u.buildPodcast(ctx, feed, feedConfig) + podcast, err := feed.Build(ctx, f, feedConfig, u.fs) if err != nil { return err } @@ -243,119 +241,3 @@ func (u *Updater) buildXML(ctx context.Context, feedConfig *config.Feed) error { return nil } - -func (u *Updater) buildPodcast(ctx context.Context, feed *model.Feed, cfg *config.Feed) (*itunes.Podcast, error) { - const ( - podsyncGenerator = "Podsync generator (support us at https://github.com/mxpv/podsync)" - defaultCategory = "TV & Film" - ) - - now := time.Now().UTC() - - p := itunes.New(feed.Title, feed.ItemURL, feed.Description, &feed.PubDate, &now) - p.Generator = podsyncGenerator - p.AddSubTitle(feed.Title) - p.AddCategory(defaultCategory, nil) - p.AddImage(feed.CoverArt) - p.IAuthor = feed.Title - p.AddSummary(feed.Description) - - if feed.Explicit { - p.IExplicit = "yes" - } else { - p.IExplicit = "no" - } - - if feed.Language != "" { - p.Language = feed.Language - } - - for i, episode := range feed.Episodes { - if episode.Status != model.EpisodeDownloaded { - // Skip episodes that are not yet downloaded - continue - } - - item := itunes.Item{ - GUID: episode.ID, - Link: episode.VideoURL, - Title: episode.Title, - Description: episode.Description, - ISubtitle: episode.Title, - IOrder: strconv.Itoa(i), - } - - pubDate := episode.PubDate - if pubDate.IsZero() { - pubDate = now - } - - item.AddPubDate(&pubDate) - - item.AddSummary(episode.Description) - item.AddImage(episode.Thumbnail) - item.AddDuration(episode.Duration) - - enclosureType := itunes.MP4 - if feed.Format == model.FormatAudio { - enclosureType = itunes.MP4 - } - - episodeName := u.episodeName(cfg, episode) - downloadURL, err := u.fs.URL(ctx, cfg.ID, episodeName) - if err != nil { - return nil, errors.Wrapf(err, "failed to obtain download URL for: %s", episodeName) - } - - item.AddEnclosure(downloadURL, enclosureType, episode.Size) - - // p.AddItem requires description to be not empty, use workaround - if item.Description == "" { - item.Description = " " - } - - if feed.Explicit { - item.IExplicit = "yes" - } else { - item.IExplicit = "no" - } - - if _, err := p.AddItem(item); err != nil { - return nil, errors.Wrapf(err, "failed to add item to podcast (id %q)", episode.ID) - } - } - - return &p, nil -} - -func (u *Updater) episodeName(feedConfig *config.Feed, episode *model.Episode) string { - ext := "mp4" - if feedConfig.Format == model.FormatAudio { - ext = "mp3" - } - - return fmt.Sprintf("%s.%s", episode.ID, ext) -} - -func (u *Updater) makeBuilder(ctx context.Context, cfg *config.Feed) (feed.Builder, error) { - var ( - provider feed.Builder - err error - ) - - info, err := link.Parse(cfg.URL) - if err != nil { - return nil, err - } - - switch info.Provider { - case link.ProviderYoutube: - provider, err = feed.NewYouTubeBuilder(u.config.Tokens.YouTube) - case link.ProviderVimeo: - provider, err = feed.NewVimeoBuilder(ctx, u.config.Tokens.Vimeo) - default: - return nil, errors.Errorf("unsupported provider %q", info.Provider) - } - - return provider, err -} diff --git a/pkg/feed/build.go b/pkg/feed/build.go new file mode 100644 index 0000000..8763211 --- /dev/null +++ b/pkg/feed/build.go @@ -0,0 +1,111 @@ +package feed + +import ( + "context" + "fmt" + "strconv" + "time" + + itunes "github.com/eduncan911/podcast" + "github.com/pkg/errors" + + "github.com/mxpv/podsync/pkg/config" + "github.com/mxpv/podsync/pkg/model" +) + +type urlProvider interface { + URL(ctx context.Context, ns string, fileName string) (string, error) +} + +func Build(ctx context.Context, feed *model.Feed, cfg *config.Feed, provider urlProvider) (*itunes.Podcast, error) { + const ( + podsyncGenerator = "Podsync generator (support us at https://github.com/mxpv/podsync)" + defaultCategory = "TV & Film" + ) + + now := time.Now().UTC() + + p := itunes.New(feed.Title, feed.ItemURL, feed.Description, &feed.PubDate, &now) + p.Generator = podsyncGenerator + p.AddSubTitle(feed.Title) + p.AddCategory(defaultCategory, nil) + p.AddImage(feed.CoverArt) + p.IAuthor = feed.Title + p.AddSummary(feed.Description) + + if feed.Explicit { + p.IExplicit = "yes" + } else { + p.IExplicit = "no" + } + + if feed.Language != "" { + p.Language = feed.Language + } + + for i, episode := range feed.Episodes { + if episode.Status != model.EpisodeDownloaded { + // Skip episodes that are not yet downloaded + continue + } + + item := itunes.Item{ + GUID: episode.ID, + Link: episode.VideoURL, + Title: episode.Title, + Description: episode.Description, + ISubtitle: episode.Title, + IOrder: strconv.Itoa(i), + } + + pubDate := episode.PubDate + if pubDate.IsZero() { + pubDate = now + } + + item.AddPubDate(&pubDate) + + item.AddSummary(episode.Description) + item.AddImage(episode.Thumbnail) + item.AddDuration(episode.Duration) + + enclosureType := itunes.MP4 + if feed.Format == model.FormatAudio { + enclosureType = itunes.MP4 + } + + episodeName := EpisodeName(cfg, episode) + downloadURL, err := provider.URL(ctx, cfg.ID, episodeName) + if err != nil { + return nil, errors.Wrapf(err, "failed to obtain download URL for: %s", episodeName) + } + + item.AddEnclosure(downloadURL, enclosureType, episode.Size) + + // p.AddItem requires description to be not empty, use workaround + if item.Description == "" { + item.Description = " " + } + + if feed.Explicit { + item.IExplicit = "yes" + } else { + item.IExplicit = "no" + } + + if _, err := p.AddItem(item); err != nil { + return nil, errors.Wrapf(err, "failed to add item to podcast (id %q)", episode.ID) + } + } + + return &p, nil +} + +func EpisodeName(feedConfig *config.Feed, episode *model.Episode) string { + ext := "mp4" + if feedConfig.Format == model.FormatAudio { + ext = "mp3" + } + + return fmt.Sprintf("%s.%s", episode.ID, ext) +} diff --git a/pkg/feed/common.go b/pkg/feed/common.go index 22153f2..58729a2 100644 --- a/pkg/feed/common.go +++ b/pkg/feed/common.go @@ -2,9 +2,11 @@ package feed import ( "context" - "errors" + + "github.com/pkg/errors" "github.com/mxpv/podsync/pkg/config" + "github.com/mxpv/podsync/pkg/link" "github.com/mxpv/podsync/pkg/model" ) @@ -16,3 +18,26 @@ var ( 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 + ) + + info, err := link.Parse(cfg.URL) + if err != nil { + return nil, err + } + + switch info.Provider { + case link.ProviderYoutube: + provider, err = NewYouTubeBuilder(tokens.YouTube) + case link.ProviderVimeo: + provider, err = NewVimeoBuilder(ctx, tokens.Vimeo) + default: + return nil, errors.Errorf("unsupported provider %q", info.Provider) + } + + return provider, err +}