rssd/main.go
Greg 95e73fd219 Read podcast list from saved file and merge new entries. Add daysAgo() selector.
Remember length of podcast episodes so we know if we need to download it again.
2019-03-14 10:28:54 -04:00

181 lines
3.2 KiB
Go

package main
import (
"log"
"os"
"strconv"
"time"
"github.com/BurntSushi/toml"
"github.com/mmcdole/gofeed"
)
type Config struct {
Urls []string
}
type Item struct {
Title, Description, Url, Filename string
Length int
}
type Podcast struct {
Title, Description, Url string
Items []Item
}
type pcList struct {
Podcasts []Podcast
}
func newpcList(confs ...string) (ret *pcList) {
ret = &pcList{}
ret.Podcasts = make([]Podcast,0)
if len(confs) > 0 {
if _, err := toml.DecodeFile("output.conf", &ret); err != nil {
log.Print("Error reading podcast list:",err)
}
}
return
}
func (p *pcList) Find(x *Podcast) (int, bool) {
for i,y := range p.Podcasts {
if y.Title == x.Title {
return i, true
}
}
return 0, false
}
func (p *pcList) Add(x *Podcast) {
if x == nil {
return
}
if i,ok := p.Find(x); ok == true {
log.Print(" Existing podcast")
p.Podcasts[i].Merge(x)
} else {
log.Print(" New podcast")
p.Podcasts = append((*p).Podcasts,*x)
}
}
func (p *Podcast) Merge(x *Podcast) {
for _,item := range x.Items {
if !p.Has(item) {
log.Print(" Appending '",item.Title,"'")
p.Items = append(p.Items,item)
}
}
}
func (p *Podcast) Has(i Item) bool {
for _,x := range p.Items {
if x.Title == i.Title {
return true
}
}
return false
}
type Selector func(*gofeed.Item) bool
func AllSelectors(ss ...Selector) Selector {
return func(i *gofeed.Item) bool {
for _, s := range ss {
if !s(i) {
return false
}
}
return true
}
}
func AnySelector(ss ...Selector) Selector {
return func(i *gofeed.Item) bool {
for _, s := range ss {
if s(i) {
return true
}
}
return false
}
}
func newerThan(t time.Time) Selector {
return func(i *gofeed.Item) bool {
if i.PublishedParsed.After(t) {
return true
}
return false
}
}
func daysAgo(x int) Selector {
d := time.Now()
return newerThan(time.Date(d.Year(),d.Month(),d.Day()-x,0,0,0,0,time.Local))
}
func toPodcast(sel Selector, url string, feed *gofeed.Feed) (ret *Podcast) {
ret = &Podcast{
Title: feed.Title,
Description: feed.Description,
Url: url,
Items: []Item{},
}
for _, i := range feed.Items {
if sel(i) {
it := Item{
Title: i.Title,
Description: i.Description,
}
for _, n := range i.Enclosures {
if n.Type == "audio/mpeg" {
it.Url = n.URL
if l, err := strconv.Atoi(n.Length); err == nil {
it.Length = l
}
}
}
ret.Items = append(ret.Items,it)
}
}
return
}
func readFeed(url string,sel Selector) *Podcast {
fp := gofeed.NewParser()
feed, err := fp.ParseURL(url)
if err != nil {
log.Print(err)
return nil
}
return toPodcast(sel,url,feed)
}
func main() {
log.Print("rssd")
log.Print("reading configuration")
var conf Config
if _, err := toml.DecodeFile("rssd.conf", &conf); err != nil {
log.Fatal("Error reading config file:",err)
}
pl := newpcList("output.conf")
sel := daysAgo(60)
for _,url := range conf.Urls {
log.Print(" -> ",url)
pl.Add(readFeed(url,sel))
}
of,err := os.Create("output.conf")
if err != nil {
log.Fatal("Cannot open output file")
}
enc := toml.NewEncoder(of)
log.Print("writing output")
enc.Encode(pl)
of.Close()
log.Print("done")
}