feeder/cmd/generate.go

274 lines
6.1 KiB
Go

package cmd
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/arenzana/podcast"
"github.com/aws/aws-sdk-go/aws/session"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"gitlab.com/iarenzana/feeder/objects"
)
var layout = "2006-01-02T15:04:05 MST"
// generateCmd represents the generate command
var generateCmd = &cobra.Command{
Use: "generate",
Short: "Generate an RSS feed",
Long: `Generate an RSS feed out of JSON episode and the settings`,
Run: func(cmd *cobra.Command, args []string) {
lastBuildTime := time.Now()
pubTime := time.Now()
var s *session.Session
log.SetFormatter(&log.TextFormatter{
DisableColors: false,
FullTimestamp: true,
})
log.SetOutput(os.Stdout)
//Create podcast
// instantiate a new Podcast
p := podcast.New(
GlobalCfg.Title,
GlobalCfg.Link,
GlobalCfg.Description,
&pubTime, &lastBuildTime,
)
p.ISubtitle = GlobalCfg.Description
p.AddSummary(GlobalCfg.Description)
p.AddImage(GlobalCfg.Image)
p.IAuthor = GlobalCfg.Author
p.AddAuthor(GlobalCfg.Author, GlobalCfg.OwnerEmail)
p.IExplicit = GlobalCfg.IsExplicit
p.Language = "es"
p.INewFeedURL = GlobalCfg.RSSURL
p.Copyright = GlobalCfg.Copyright
p.IOwner = &podcast.Author{
Email: GlobalCfg.OwnerEmail,
Name: GlobalCfg.Author,
}
p.AddAtomLink(GlobalCfg.RSSURL)
for _, category := range GlobalCfg.Categories {
p.AddCategory(category, nil)
}
//Gather podcast items
episodesInJSON, err := getPodcastEpisodesFromJSON(GlobalCfg.Items)
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error getting episodes from JSON")
os.Exit(-1)
}
podcastEpisodes, err := getPodcastEpisodesFromItem(episodesInJSON)
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error getting episodes from config")
os.Exit(-1)
}
for _, episode := range podcastEpisodes {
if _, err := p.AddItem(episode); err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Item validation error")
os.Exit(-1)
}
}
if verbose {
if err := p.Encode(os.Stdout); err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error displaying RSS")
os.Exit(-1)
}
}
//Save RSS to file
err = backupFile(GlobalCfg.RSSOut)
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error backing up current RSS feed")
os.Exit(-1)
}
saveToFile(p, GlobalCfg.RSSOut)
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error saving RSS file. Backup still in place")
os.Exit(-1)
}
//Upload file if desired
if upload {
s, err = createS3Session()
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error establishing S3 session")
os.Exit(-1)
}
rssFileName := filepath.Base(GlobalCfg.RSSOut)
rssURL, err := uploadFile(s, GlobalCfg.RSSOut, rssFileName)
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error uploading to S3")
os.Exit(-1)
}
if verbose {
log.WithFields(log.Fields{"payload": rssURL}).Info("RSS Feed successfully uploaded to S3")
}
}
//Generate and upload Index file
if renderWeb {
err = renderIndexHTML(GlobalCfg, episodesInJSON)
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error rendering HTML.")
os.Exit(-1)
}
if upload {
indexURL, err := uploadFile(s, "index.html", "index.html")
if err != nil {
log.WithFields(log.Fields{"payload": err}).Error("Error uploading HTML to S3")
os.Exit(-1)
}
if verbose {
log.WithFields(log.Fields{"payload": indexURL}).Info("HTML successfully uploaded to S3")
}
}
}
},
}
func init() {
rootCmd.AddCommand(generateCmd)
}
func backupFile(file string) error {
layout := "02150405"
backupFile := fmt.Sprintf("%v.%v", file, time.Now().Format(layout))
if _, err := os.Stat(file); err == nil {
source, err := os.Open(file)
if err != nil {
return err
}
defer source.Close()
destination, err := os.Create(backupFile)
if err != nil {
return err
}
defer destination.Close()
_, err = io.Copy(destination, source)
if err != nil {
return fmt.Errorf("Error generating backup file - %v", err)
}
}
return nil
}
func saveToFile(rss podcast.Podcast, file string) error {
out, err := os.Create(file)
if err != nil {
return err
}
defer out.Close()
if err := rss.Encode(out); err != nil {
return err
}
return out.Sync()
}
func getPodcastEpisodesFromJSON(file string) (objects.Items, error) {
var items objects.Items
jsonFile, err := os.Open(file)
if err != nil {
return items, fmt.Errorf("Error opening episodes JSON file - %v", err)
}
if verbose {
log.WithFields(log.Fields{"payload": ""}).Info("Opened episodes JSON file")
}
defer jsonFile.Close()
byteValue, err := ioutil.ReadAll(jsonFile)
if err != nil {
return items, err
}
json.Unmarshal(byteValue, &items)
if len(items.Episodes) < 1 {
return items, fmt.Errorf("No episodes found")
}
return items, nil
}
func getPodcastEpisodesFromItem(items objects.Items) ([]podcast.Item, error) {
var outputEpisodes []podcast.Item
for _, episode := range items.Episodes {
item, err := createItem(episode)
if err != nil {
return nil, err
}
outputEpisodes = append(outputEpisodes, item)
}
return outputEpisodes, nil
}
func createItem(episode objects.Episode) (podcast.Item, error) {
pubdate, err := time.Parse(layout, episode.PubDate)
if err != nil {
return podcast.Item{}, err
}
item := podcast.Item{
Title: episode.Title,
Description: episode.Description,
ISubtitle: episode.Subtitle,
PubDate: &pubdate,
}
item.IEpisode = episode.Episode
if len(episode.GUID) != 0 {
item.GUID = episode.GUID
}
item.AddImage(GlobalCfg.Image)
item.AddSummary(episode.Summary)
if strings.Contains(episode.Duration, ":") {
item.IDuration = episode.Duration
} else {
i, err := strconv.ParseInt(episode.Duration, 10, 64)
if err != nil {
return podcast.Item{}, err
}
item.AddDuration(i)
}
item.AddEnclosure(episode.FileURL, podcast.MP3, episode.Bytes)
return item, nil
}