opml tweaks & fixes

This commit is contained in:
Nazar Kanaev 2021-03-19 00:00:23 +00:00
parent 62e2ca4c16
commit 391ce61362
5 changed files with 42 additions and 50 deletions

View File

@ -8,8 +8,8 @@ import (
type Folder struct { type Folder struct {
Title string Title string
Folders []*Folder Folders []Folder
Feeds []*Feed Feeds []Feed
} }
type Feed struct { type Feed struct {
@ -18,16 +18,8 @@ type Feed struct {
SiteUrl string SiteUrl string
} }
func NewFolder(title string) *Folder { func (f Folder) AllFeeds() []Feed {
return &Folder{ feeds := make([]Feed, 0)
Title: title,
Folders: make([]*Folder, 0),
Feeds: make([]*Feed, 0),
}
}
func (f *Folder) AllFeeds() []*Feed {
feeds := make([]*Feed, 0)
feeds = append(feeds, f.Feeds...) feeds = append(feeds, f.Feeds...)
for _, subfolder := range f.Folders { for _, subfolder := range f.Folders {
feeds = append(feeds, subfolder.AllFeeds()...) feeds = append(feeds, subfolder.AllFeeds()...)
@ -39,7 +31,7 @@ var e = html.EscapeString
var indent = " " var indent = " "
var nl = "\n" var nl = "\n"
func (f *Folder) outline(level int) string { func (f Folder) outline(level int) string {
builder := strings.Builder{} builder := strings.Builder{}
prefix := strings.Repeat(indent, level) prefix := strings.Repeat(indent, level)
@ -58,14 +50,14 @@ func (f *Folder) outline(level int) string {
return builder.String() return builder.String()
} }
func (f *Feed) outline(level int) string { func (f Feed) outline(level int) string {
return strings.Repeat(indent, level) + fmt.Sprintf( return strings.Repeat(indent, level) + fmt.Sprintf(
`<outline type="rss" text="%s" xmlUrl="%s" htmlUrl="%s"/>` + nl, `<outline type="rss" text="%s" xmlUrl="%s" htmlUrl="%s"/>` + nl,
e(f.Title), e(f.FeedUrl), e(f.SiteUrl), e(f.Title), e(f.FeedUrl), e(f.SiteUrl),
) )
} }
func (f *Folder) OPML() string { func (f Folder) OPML() string {
builder := strings.Builder{} builder := strings.Builder{}
builder.WriteString(`<?xml version="1.0" encoding="UTF-8"?>` + nl) builder.WriteString(`<?xml version="1.0" encoding="UTF-8"?>` + nl)
builder.WriteString(`<opml version="1.1">` + nl) builder.WriteString(`<opml version="1.1">` + nl)

View File

@ -7,31 +7,31 @@ import (
func TestOPML(t *testing.T) { func TestOPML(t *testing.T) {
have := (&Folder{ have := (Folder{
Title: "", Title: "",
Feeds: []*Feed{ Feeds: []Feed{
&Feed{ Feed{
Title: "title1", Title: "title1",
FeedUrl: "https://baz.com/feed.xml", FeedUrl: "https://baz.com/feed.xml",
SiteUrl: "https://baz.com/", SiteUrl: "https://baz.com/",
}, },
}, },
Folders: []*Folder{ Folders: []Folder{
&Folder{ Folder{
Title: "sub", Title: "sub",
Feeds: []*Feed{ Feeds: []Feed{
&Feed{ Feed{
Title: "subtitle1", Title: "subtitle1",
FeedUrl: "https://foo.com/feed.xml", FeedUrl: "https://foo.com/feed.xml",
SiteUrl: "https://foo.com/", SiteUrl: "https://foo.com/",
}, },
&Feed{ Feed{
Title: "&>", Title: "&>",
FeedUrl: "https://bar.com/feed.xml", FeedUrl: "https://bar.com/feed.xml",
SiteUrl: "https://bar.com/", SiteUrl: "https://bar.com/",
}, },
}, },
Folders: []*Folder{}, Folders: []Folder{},
}, },
}, },
}).OPML() }).OPML()

View File

@ -18,11 +18,11 @@ type outline struct {
Outlines []outline `xml:"outline,omitempty"` Outlines []outline `xml:"outline,omitempty"`
} }
func buildFolder(title string, outlines []outline) *Folder { func buildFolder(title string, outlines []outline) Folder {
folder := NewFolder(title) folder := Folder{Title: title}
for _, outline := range outlines { for _, outline := range outlines {
if outline.Type == "rss" { if outline.Type == "rss" {
folder.Feeds = append(folder.Feeds, &Feed{ folder.Feeds = append(folder.Feeds, Feed{
Title: outline.Title, Title: outline.Title,
FeedUrl: outline.FeedUrl, FeedUrl: outline.FeedUrl,
SiteUrl: outline.SiteUrl, SiteUrl: outline.SiteUrl,
@ -35,7 +35,7 @@ func buildFolder(title string, outlines []outline) *Folder {
return folder return folder
} }
func Parse(r io.Reader) (*Folder, error) { func Parse(r io.Reader) (Folder, error) {
val := new(opml) val := new(opml)
decoder := xml.NewDecoder(r) decoder := xml.NewDecoder(r)
decoder.Entity = xml.HTMLEntity decoder.Entity = xml.HTMLEntity
@ -43,7 +43,7 @@ func Parse(r io.Reader) (*Folder, error) {
err := decoder.Decode(&val) err := decoder.Decode(&val)
if err != nil { if err != nil {
return nil, err return Folder{}, err
} }
return buildFolder("", val.Outlines), nil return buildFolder("", val.Outlines), nil
} }

View File

@ -24,31 +24,30 @@ func TestParse(t *testing.T) {
</body> </body>
</opml> </opml>
`)) `))
want := &Folder{ want := Folder{
Title: "", Title: "",
Feeds: []*Feed{ Feeds: []Feed{
&Feed{ Feed{
Title: "title1", Title: "title1",
FeedUrl: "https://baz.com/feed.xml", FeedUrl: "https://baz.com/feed.xml",
SiteUrl: "https://baz.com/", SiteUrl: "https://baz.com/",
}, },
}, },
Folders: []*Folder{ Folders: []Folder{
&Folder{ Folder{
Title: "sub", Title: "sub",
Feeds: []*Feed{ Feeds: []Feed{
&Feed{ Feed{
Title: "subtitle1", Title: "subtitle1",
FeedUrl: "https://foo.com/feed.xml", FeedUrl: "https://foo.com/feed.xml",
SiteUrl: "https://foo.com/", SiteUrl: "https://foo.com/",
}, },
&Feed{ Feed{
Title: "&>", Title: "&>",
FeedUrl: "https://bar.com/feed.xml", FeedUrl: "https://bar.com/feed.xml",
SiteUrl: "https://bar.com/", SiteUrl: "https://bar.com/",
}, },
}, },
Folders: []*Folder{},
}, },
}, },
} }

View File

@ -2,17 +2,18 @@ package server
import ( import (
"encoding/json" "encoding/json"
"github.com/nkanaev/yarr/src/assets"
"github.com/nkanaev/yarr/src/auth"
"github.com/nkanaev/yarr/src/router"
"github.com/nkanaev/yarr/src/storage"
"github.com/nkanaev/yarr/src/opml"
"github.com/nkanaev/yarr/src/worker"
"io/ioutil" "io/ioutil"
"log" "log"
"math" "math"
"net/http" "net/http"
"reflect" "reflect"
"github.com/nkanaev/yarr/src/assets"
"github.com/nkanaev/yarr/src/auth"
"github.com/nkanaev/yarr/src/opml"
"github.com/nkanaev/yarr/src/router"
"github.com/nkanaev/yarr/src/storage"
"github.com/nkanaev/yarr/src/worker"
) )
func (s *Server) handler() http.Handler { func (s *Server) handler() http.Handler {
@ -331,14 +332,13 @@ func (s *Server) handleOPMLImport(c *router.Context) {
c.Out.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
for _, f := range doc.Feeds { for _, f := range doc.Feeds {
s.db.CreateFeed(f.Title, "", f.SiteUrl, f.FeedUrl, nil) s.db.CreateFeed(f.Title, "", f.SiteUrl, f.FeedUrl, nil)
} }
for _, f := range doc.Folders { for _, f := range doc.Folders {
folder := s.db.CreateFolder(f.Title) folder := s.db.CreateFolder(f.Title)
for _, ff := range f.AllFeeds() { for _, ff := range f.AllFeeds() {
s.db.CreateFeed(f.Title, "", ff.SiteUrl, ff.FeedUrl, &folder.Id) s.db.CreateFeed(ff.Title, "", ff.SiteUrl, ff.FeedUrl, &folder.Id)
} }
} }
@ -354,13 +354,13 @@ func (s *Server) handleOPMLExport(c *router.Context) {
c.Out.Header().Set("Content-Type", "application/xml; charset=utf-8") c.Out.Header().Set("Content-Type", "application/xml; charset=utf-8")
c.Out.Header().Set("Content-Disposition", `attachment; filename="subscriptions.opml"`) c.Out.Header().Set("Content-Disposition", `attachment; filename="subscriptions.opml"`)
doc := opml.NewFolder("") doc := opml.Folder{}
feedsByFolderID := make(map[int64][]*storage.Feed) feedsByFolderID := make(map[int64][]*storage.Feed)
for _, feed := range s.db.ListFeeds() { for _, feed := range s.db.ListFeeds() {
feed := feed feed := feed
if feed.FolderId == nil { if feed.FolderId == nil {
doc.Feeds = append(doc.Feeds, &opml.Feed{ doc.Feeds = append(doc.Feeds, opml.Feed{
Title: feed.Title, Title: feed.Title,
FeedUrl: feed.FeedLink, FeedUrl: feed.FeedLink,
SiteUrl: feed.Link, SiteUrl: feed.Link,
@ -376,14 +376,15 @@ func (s *Server) handleOPMLExport(c *router.Context) {
if len(folderFeeds) == 0 { if len(folderFeeds) == 0 {
continue continue
} }
opmlfolder := opml.NewFolder(folder.Title) opmlfolder := opml.Folder{Title: folder.Title}
for _, feed := range folderFeeds { for _, feed := range folderFeeds {
opmlfolder.Feeds = append(opmlfolder.Feeds, &opml.Feed{ opmlfolder.Feeds = append(opmlfolder.Feeds, opml.Feed{
Title: feed.Title, Title: feed.Title,
FeedUrl: feed.FeedLink, FeedUrl: feed.FeedLink,
SiteUrl: feed.Link, SiteUrl: feed.Link,
}) })
} }
doc.Folders = append(doc.Folders, opmlfolder)
} }
c.Out.Write([]byte(doc.OPML())) c.Out.Write([]byte(doc.OPML()))