2019-03-13 15:47:38 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"os"
|
2019-03-14 10:28:54 -04:00
|
|
|
"strconv"
|
2019-03-13 15:47:38 -04:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/BurntSushi/toml"
|
|
|
|
"github.com/mmcdole/gofeed"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
Urls []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Item struct {
|
|
|
|
Title, Description, Url, Filename string
|
2019-03-14 10:28:54 -04:00
|
|
|
Length int
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type Podcast struct {
|
2019-03-14 10:28:54 -04:00
|
|
|
Title, Description, Url string
|
2019-03-13 15:47:38 -04:00
|
|
|
Items []Item
|
|
|
|
}
|
|
|
|
|
|
|
|
type pcList struct {
|
|
|
|
Podcasts []Podcast
|
|
|
|
}
|
|
|
|
|
2019-03-14 10:28:54 -04:00
|
|
|
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
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
|
2019-03-14 10:28:54 -04:00
|
|
|
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
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
|
2019-03-14 10:28:54 -04:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-13 15:47:38 -04:00
|
|
|
|
2019-03-14 10:28:54 -04:00
|
|
|
func (p *Podcast) Has(i Item) bool {
|
|
|
|
for _,x := range p.Items {
|
|
|
|
if x.Title == i.Title {
|
2019-03-13 15:47:38 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-03-14 10:28:54 -04:00
|
|
|
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
|
|
|
|
}
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
2019-03-14 10:28:54 -04:00
|
|
|
return false
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newerThan(t time.Time) Selector {
|
|
|
|
return func(i *gofeed.Item) bool {
|
|
|
|
if i.PublishedParsed.After(t) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-14 10:28:54 -04:00
|
|
|
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{},
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
for _, i := range feed.Items {
|
2019-03-14 10:28:54 -04:00
|
|
|
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)
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
}
|
2019-03-14 10:28:54 -04:00
|
|
|
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)
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2019-03-14 10:28:54 -04:00
|
|
|
pl := newpcList("output.conf")
|
2019-03-13 15:47:38 -04:00
|
|
|
|
2019-03-14 10:28:54 -04:00
|
|
|
sel := daysAgo(60)
|
2019-03-13 15:47:38 -04:00
|
|
|
for _,url := range conf.Urls {
|
|
|
|
log.Print(" -> ",url)
|
2019-03-14 10:28:54 -04:00
|
|
|
pl.Add(readFeed(url,sel))
|
2019-03-13 15:47:38 -04:00
|
|
|
}
|
|
|
|
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")
|
|
|
|
}
|