Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37408af647 | ||
|
|
6f7ff06e3f | ||
|
|
6ce1197f72 | ||
|
|
db79a43e95 | ||
|
|
2518c3f263 | ||
|
|
9c4c0da004 | ||
|
|
e388723199 |
2
.gitignore
vendored
Normal file → Executable file
2
.gitignore
vendored
Normal file → Executable file
@@ -3,3 +3,5 @@ exec-rssmon3
|
|||||||
**.sw*
|
**.sw*
|
||||||
**/testdata
|
**/testdata
|
||||||
**/._*
|
**/._*
|
||||||
|
**/exec-*
|
||||||
|
exec-*
|
||||||
|
|||||||
0
config/config.go
Normal file → Executable file
0
config/config.go
Normal file → Executable file
0
config/encode.go
Normal file → Executable file
0
config/encode.go
Normal file → Executable file
0
config/encode_test.go
Normal file → Executable file
0
config/encode_test.go
Normal file → Executable file
0
config/new.go
Normal file → Executable file
0
config/new.go
Normal file → Executable file
0
config/new_test.go
Normal file → Executable file
0
config/new_test.go
Normal file → Executable file
0
config/stoppable.go
Normal file → Executable file
0
config/stoppable.go
Normal file → Executable file
0
handlers/handler.go
Normal file → Executable file
0
handlers/handler.go
Normal file → Executable file
234
handlers/podcast/main.go
Executable file
234
handlers/podcast/main.go
Executable file
@@ -0,0 +1,234 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"local/args"
|
||||||
|
"local/storage"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mmcdole/gofeed"
|
||||||
|
)
|
||||||
|
|
||||||
|
const sessionHeader = "X-Transmission-Session-Id"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
url string
|
||||||
|
vpntor string
|
||||||
|
outdir string
|
||||||
|
interval time.Duration
|
||||||
|
last time.Time
|
||||||
|
db storage.DB
|
||||||
|
ctx context.Context
|
||||||
|
can context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config, err := config()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
log.Println(config)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if err := mainLoop(config); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mainLoop(config *Config) error {
|
||||||
|
block := config.interval - time.Since(config.last)
|
||||||
|
log.Printf("Blocking %v", block)
|
||||||
|
select {
|
||||||
|
case <-time.After(block):
|
||||||
|
if err := pull(config.db, config.vpntor, config.outdir, config.url); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
config.last = time.Now()
|
||||||
|
case <-config.ctx.Done():
|
||||||
|
if err := config.ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func config() (*Config, error) {
|
||||||
|
as := args.NewArgSet()
|
||||||
|
as.Append(args.STRING, "url", "url of rss feed", "http://192.168.0.86:33419/api/tag/podcast")
|
||||||
|
as.Append(args.STRING, "vpntor", "url of vpntor", "http://192.168.0.86:9091/transmission/rpc")
|
||||||
|
as.Append(args.DURATION, "interval", "interval to check feed", "30m")
|
||||||
|
as.Append(args.STRING, "outdir", "save dir", "/data/completed-rss")
|
||||||
|
as.Append(args.STRING, "db", "db type", "map")
|
||||||
|
as.Append(args.STRING, "addr", "db addr", "")
|
||||||
|
as.Append(args.STRING, "user", "db user", "")
|
||||||
|
as.Append(args.STRING, "pass", "db pass", "")
|
||||||
|
if err := as.Parse(); err != nil {
|
||||||
|
return &Config{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := storage.New(
|
||||||
|
storage.TypeFromString(as.Get("db").GetString()),
|
||||||
|
as.Get("addr").GetString(),
|
||||||
|
as.Get("user").GetString(),
|
||||||
|
as.Get("pass").GetString(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, can := context.WithCancel(context.Background())
|
||||||
|
return &Config{
|
||||||
|
url: as.Get("url").GetString(),
|
||||||
|
vpntor: as.Get("vpntor").GetString(),
|
||||||
|
interval: as.Get("interval").GetDuration(),
|
||||||
|
outdir: as.Get("outdir").GetString(),
|
||||||
|
db: db,
|
||||||
|
ctx: ctx,
|
||||||
|
can: can,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pull(db storage.DB, vpntor, outdir, url string) error {
|
||||||
|
gofeed, err := getGoFeed(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("feed: %v", gofeed.Title)
|
||||||
|
for _, item := range gofeed.Items {
|
||||||
|
if ok, err := isDone(db, item.Link); err != nil {
|
||||||
|
return err
|
||||||
|
} else if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s, err := getItemContent(item)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := handle(vpntor, outdir, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := db.Set(item.Link, []byte{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGoFeed(url string) (*gofeed.Feed, error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return gofeed.NewParser().Parse(resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getItemContent(item *gofeed.Item) (string, error) {
|
||||||
|
s := item.Description
|
||||||
|
if s == "" {
|
||||||
|
s = item.Content
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
resp, err := http.Get(item.Link)
|
||||||
|
if err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
s = string(b)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDone(db storage.DB, url string) (bool, error) {
|
||||||
|
_, err := db.Get(url)
|
||||||
|
if err == storage.ErrNotFound {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(vpntor, outdir, content string) error {
|
||||||
|
links := findMagnets(content)
|
||||||
|
sort.Strings(links)
|
||||||
|
for i := range links {
|
||||||
|
link := links[i]
|
||||||
|
if i > 0 && link == links[i-1] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println(link)
|
||||||
|
if err := fetch(link, outdir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMagnets(s string) []string {
|
||||||
|
magnetRegexp := regexp.MustCompile(`http[^"]*[0-9]+\.mp3`)
|
||||||
|
return magnetRegexp.FindAllString(s, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetch(link, outdir string) error {
|
||||||
|
out := path.Join(outdir, path.Base(link))
|
||||||
|
if _, err := os.Stat(out); err == nil {
|
||||||
|
return nil
|
||||||
|
} else if os.IsNotExist(err) {
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(link)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
f, err := os.Create(out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
io.Copy(f, resp.Body)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildReqBody(outdir, magnet string) io.Reader {
|
||||||
|
return strings.NewReader(fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"method": "torrent-add",
|
||||||
|
"arguments": {
|
||||||
|
"filename": %q,
|
||||||
|
"download-dir": %q
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, magnet, outdir))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSessionID(vpntor string) (string, error) {
|
||||||
|
resp, err := http.Get(vpntor)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
id := resp.Header.Get(sessionHeader)
|
||||||
|
if id == "" {
|
||||||
|
err = errors.New("session id header not found")
|
||||||
|
}
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
12
handlers/torrent/main.go
Normal file → Executable file
12
handlers/torrent/main.go
Normal file → Executable file
@@ -11,6 +11,7 @@ import (
|
|||||||
"local/storage"
|
"local/storage"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -99,12 +100,12 @@ func config() (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func pull(db storage.DB, vpntor, outdir, url string) error {
|
func pull(db storage.DB, vpntor, outdir, url string) error {
|
||||||
gofeed, err := getGoFeed(url)
|
gfeed, err := getGoFeed(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("feed: %v", gofeed.Title)
|
log.Printf("feed: %v", gfeed.Title)
|
||||||
for _, item := range gofeed.Items {
|
for _, item := range gfeed.Items {
|
||||||
if ok, err := isDone(db, item.Link); err != nil {
|
if ok, err := isDone(db, item.Link); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if ok {
|
} else if ok {
|
||||||
@@ -114,7 +115,10 @@ func pull(db storage.DB, vpntor, outdir, url string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := handle(vpntor, outdir, s); err != nil {
|
if item.Author == nil {
|
||||||
|
item.Author = &gofeed.Person{Name: "."}
|
||||||
|
}
|
||||||
|
if err := handle(vpntor, path.Join(outdir, item.Author.Name), s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := db.Set(item.Link, []byte{}); err != nil {
|
if err := db.Set(item.Link, []byte{}); err != nil {
|
||||||
|
|||||||
0
handlers/torrent/main_test.go
Normal file → Executable file
0
handlers/torrent/main_test.go
Normal file → Executable file
0
monitor/item.go
Normal file → Executable file
0
monitor/item.go
Normal file → Executable file
0
monitor/item_test.go
Normal file → Executable file
0
monitor/item_test.go
Normal file → Executable file
0
monitor/monitor.go
Normal file → Executable file
0
monitor/monitor.go
Normal file → Executable file
0
monitor/monitor_test.go
Normal file → Executable file
0
monitor/monitor_test.go
Normal file → Executable file
0
monitor/queue.go
Normal file → Executable file
0
monitor/queue.go
Normal file → Executable file
0
monitor/queue_test.go
Normal file → Executable file
0
monitor/queue_test.go
Normal file → Executable file
9
rss/feed.go
Normal file → Executable file
9
rss/feed.go
Normal file → Executable file
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mmcdole/gofeed"
|
"github.com/mmcdole/gofeed"
|
||||||
@@ -21,6 +22,7 @@ type Feed struct {
|
|||||||
TitleFilter string
|
TitleFilter string
|
||||||
ContentFilter string
|
ContentFilter string
|
||||||
Tags []string
|
Tags []string
|
||||||
|
Copyright string
|
||||||
}
|
}
|
||||||
|
|
||||||
func SubmitFeed(f *Feed) error {
|
func SubmitFeed(f *Feed) error {
|
||||||
@@ -72,7 +74,7 @@ func (f *Feed) Pull() error {
|
|||||||
|
|
||||||
itemTSs := []*time.Time{}
|
itemTSs := []*time.Time{}
|
||||||
for _, i := range gofeed.Items {
|
for _, i := range gofeed.Items {
|
||||||
item, err := newItem(i, f.ContentFilter)
|
item, err := newItem(i, f.ContentFilter, f.Copyright)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[Pull]", err)
|
log.Println("[Pull]", err)
|
||||||
continue
|
continue
|
||||||
@@ -83,9 +85,10 @@ func (f *Feed) Pull() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if ok := regexp.MustCompile(f.TitleFilter).MatchString(item.Title); !ok {
|
if ok := regexp.MustCompile(f.TitleFilter).MatchString(item.Title); !ok {
|
||||||
log.Println("[Pull]", "Skipping bad titled item")
|
log.Printf("[Pull] Skipping bad titled item: %v doesn't match /%v/", item.Title, f.TitleFilter)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
log.Printf("[Pull] Saving item %v for %v /%v/", f.Key, f.URL, f.TitleFilter)
|
||||||
if err := item.save(f.Key); err != nil {
|
if err := item.save(f.Key); err != nil {
|
||||||
log.Println("[Pull]", err)
|
log.Println("[Pull]", err)
|
||||||
continue
|
continue
|
||||||
@@ -123,7 +126,7 @@ func (f *Feed) Items(limit int) ([]*Item, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *Feed) List(limit int) ([]string, error) {
|
func (f *Feed) List(limit int) ([]string, error) {
|
||||||
keys, err := config.Values().DB.List([]string{nsItems, f.Key})
|
keys, err := config.Values().DB.List([]string{nsItems, f.Key}, " ", "}}}}}", strconv.Itoa(limit), "-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
34
rss/feed_test.go
Normal file → Executable file
34
rss/feed_test.go
Normal file → Executable file
@@ -177,3 +177,37 @@ func TestRSSFeedPull(t *testing.T) {
|
|||||||
t.Fatal(i)
|
t.Fatal(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRSSFeedListLimitedDescending(t *testing.T) {
|
||||||
|
initRSSFeed()
|
||||||
|
|
||||||
|
s := mockRSS()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
f := newFeed("key")
|
||||||
|
f.TitleFilter = "50."
|
||||||
|
f.ContentFilter = "b"
|
||||||
|
f.Tags = []string{"c"}
|
||||||
|
f.URL = s.URL
|
||||||
|
|
||||||
|
log.SetOutput(bytes.NewBuffer(nil))
|
||||||
|
defer log.SetOutput(os.Stderr)
|
||||||
|
if err := f.Pull(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
itemKeys, err := f.List(5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(itemKeys) != 5 {
|
||||||
|
t.Fatal(len(itemKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range itemKeys {
|
||||||
|
if i > 0 && itemKeys[i] > itemKeys[i-1] {
|
||||||
|
t.Error(itemKeys[i], ">", itemKeys[i-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
7
rss/item.go
Normal file → Executable file
7
rss/item.go
Normal file → Executable file
@@ -20,16 +20,18 @@ type Item struct {
|
|||||||
Link string
|
Link string
|
||||||
Content string
|
Content string
|
||||||
TS time.Time
|
TS time.Time
|
||||||
|
Copyright string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Items []*Item
|
type Items []*Item
|
||||||
|
|
||||||
func newItem(i *gofeed.Item, contentFilter string) (*Item, error) {
|
func newItem(i *gofeed.Item, contentFilter, copyright string) (*Item, error) {
|
||||||
item := &Item{
|
item := &Item{
|
||||||
Title: i.Title,
|
Title: i.Title,
|
||||||
Link: i.Link,
|
Link: i.Link,
|
||||||
Content: i.Content,
|
Content: i.Content,
|
||||||
TS: latestTSPtr(i.UpdatedParsed, i.PublishedParsed),
|
TS: latestTSPtr(i.UpdatedParsed, i.PublishedParsed),
|
||||||
|
Copyright: copyright,
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Content == "" {
|
if item.Content == "" {
|
||||||
@@ -47,6 +49,9 @@ func newItem(i *gofeed.Item, contentFilter string) (*Item, error) {
|
|||||||
}
|
}
|
||||||
item.Content = string(b)
|
item.Content = string(b)
|
||||||
}
|
}
|
||||||
|
for _, enclosure := range i.Enclosures {
|
||||||
|
item.Content += fmt.Sprintf(`<br><a href="%s">%s</a>`, enclosure.URL, enclosure.URL)
|
||||||
|
}
|
||||||
if unescaped, err := url.QueryUnescape(item.Content); err == nil {
|
if unescaped, err := url.QueryUnescape(item.Content); err == nil {
|
||||||
item.Content = unescaped
|
item.Content = unescaped
|
||||||
}
|
}
|
||||||
|
|||||||
4
rss/item_test.go
Normal file → Executable file
4
rss/item_test.go
Normal file → Executable file
@@ -30,12 +30,12 @@ func TestRSSItemNewEncodeDecode(t *testing.T) {
|
|||||||
gofeed.Items[0].Content = ""
|
gofeed.Items[0].Content = ""
|
||||||
gofeed.Items[0].Description = ""
|
gofeed.Items[0].Description = ""
|
||||||
|
|
||||||
item, err := newItem(gofeed.Items[0], ".*")
|
item, err := newItem(gofeed.Items[0], ".*", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
itemB, err := newItem(gofeed.Items[0], "Podcast")
|
itemB, err := newItem(gofeed.Items[0], "Podcast", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
0
rss/rss.go
Normal file → Executable file
0
rss/rss.go
Normal file → Executable file
0
rss/rss_test.go
Normal file → Executable file
0
rss/rss_test.go
Normal file → Executable file
1
rss/serialize.go
Normal file → Executable file
1
rss/serialize.go
Normal file → Executable file
@@ -43,6 +43,7 @@ func WriteFeed(w io.Writer, tag string, items []*Item) error {
|
|||||||
Title: item.Title,
|
Title: item.Title,
|
||||||
Link: &feeds.Link{Href: item.Link},
|
Link: &feeds.Link{Href: item.Link},
|
||||||
Description: item.Content,
|
Description: item.Content,
|
||||||
|
Author: &feeds.Author{Name: item.Copyright},
|
||||||
Created: item.TS,
|
Created: item.TS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
rss/serialize_test.go
Normal file → Executable file
1
rss/serialize_test.go
Normal file → Executable file
@@ -47,6 +47,7 @@ func TestRSSWriteFeed(t *testing.T) {
|
|||||||
f.ContentFilter = "b"
|
f.ContentFilter = "b"
|
||||||
f.Tags = []string{"c"}
|
f.Tags = []string{"c"}
|
||||||
f.URL = s.URL
|
f.URL = s.URL
|
||||||
|
f.Copyright = "copyright"
|
||||||
|
|
||||||
log.SetOutput(bytes.NewBuffer(nil))
|
log.SetOutput(bytes.NewBuffer(nil))
|
||||||
defer log.SetOutput(os.Stderr)
|
defer log.SetOutput(os.Stderr)
|
||||||
|
|||||||
6119
rss/testdata/rss.xml
vendored
Executable file
6119
rss/testdata/rss.xml
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
rss/testdata/rss.xml.REMOVED.git-id
vendored
1
rss/testdata/rss.xml.REMOVED.git-id
vendored
@@ -1 +0,0 @@
|
|||||||
d36f6c94dfbbaaeac339bfaec9d0dd13b0ff099b
|
|
||||||
0
server/new.go
Normal file → Executable file
0
server/new.go
Normal file → Executable file
0
server/new_test.go
Normal file → Executable file
0
server/new_test.go
Normal file → Executable file
9
server/routes.go
Normal file → Executable file
9
server/routes.go
Normal file → Executable file
@@ -9,6 +9,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,6 +51,10 @@ func (s *Server) tag(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.notFound(w, r)
|
s.notFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
limit, err := strconv.Atoi(r.URL.Query().Get("n"))
|
||||||
|
if err != nil {
|
||||||
|
limit = 20
|
||||||
|
}
|
||||||
tag := regexp.MustCompile("^.*\\/").ReplaceAllString(r.URL.Path, "")
|
tag := regexp.MustCompile("^.*\\/").ReplaceAllString(r.URL.Path, "")
|
||||||
feeds, err := rss.TaggedFeeds(tag)
|
feeds, err := rss.TaggedFeeds(tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -58,7 +63,7 @@ func (s *Server) tag(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
items := []*rss.Item{}
|
items := []*rss.Item{}
|
||||||
for _, feed := range feeds {
|
for _, feed := range feeds {
|
||||||
feedItems, err := feed.Items(20)
|
feedItems, err := feed.Items(limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.error(w, r, err)
|
s.error(w, r, err)
|
||||||
}
|
}
|
||||||
@@ -79,6 +84,7 @@ func (s *Server) feed(w http.ResponseWriter, r *http.Request) {
|
|||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Interval string `json:"refresh"`
|
Interval string `json:"refresh"`
|
||||||
TitleFilter string `json:"items"`
|
TitleFilter string `json:"items"`
|
||||||
|
Copyright string `json:"copyright"`
|
||||||
ContentFilter string `json:"content"`
|
ContentFilter string `json:"content"`
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
}
|
}
|
||||||
@@ -97,6 +103,7 @@ func (s *Server) feed(w http.ResponseWriter, r *http.Request) {
|
|||||||
TitleFilter: putFeed.TitleFilter,
|
TitleFilter: putFeed.TitleFilter,
|
||||||
ContentFilter: putFeed.ContentFilter,
|
ContentFilter: putFeed.ContentFilter,
|
||||||
Tags: putFeed.Tags,
|
Tags: putFeed.Tags,
|
||||||
|
Copyright: putFeed.Copyright,
|
||||||
}
|
}
|
||||||
if err := rss.SubmitFeed(f); err != nil {
|
if err := rss.SubmitFeed(f); err != nil {
|
||||||
s.error(w, r, err)
|
s.error(w, r, err)
|
||||||
|
|||||||
0
server/routes_test.go
Normal file → Executable file
0
server/routes_test.go
Normal file → Executable file
0
server/server.go
Normal file → Executable file
0
server/server.go
Normal file → Executable file
0
server/server_test.go
Normal file → Executable file
0
server/server_test.go
Normal file → Executable file
0
vendor/vendor.json
vendored
Normal file → Executable file
0
vendor/vendor.json
vendored
Normal file → Executable file
Reference in New Issue
Block a user