aboutsummaryrefslogtreecommitdiffstats
path: root/internal/exporter
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-14 09:42:14 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-14 09:42:14 -0800
commit6fa13a06411048f3217397f4285b3e64e7b9ee58 (patch)
treeaf5ec83884bb45a10d51cf5be3ae895ebf1bcff8 /internal/exporter
parent23947045c011e84149bc1b9d48805e57bb0bb3ba (diff)
downloadneko-6fa13a06411048f3217397f4285b3e64e7b9ee58.tar.gz
neko-6fa13a06411048f3217397f4285b3e64e7b9ee58.tar.bz2
neko-6fa13a06411048f3217397f4285b3e64e7b9ee58.zip
feature: implement full OPML and Text import/export (fixing NK-r6nhj0)
Diffstat (limited to 'internal/exporter')
-rw-r--r--internal/exporter/exporter.go91
1 files changed, 77 insertions, 14 deletions
diff --git a/internal/exporter/exporter.go b/internal/exporter/exporter.go
index 9172fec..2d32fdb 100644
--- a/internal/exporter/exporter.go
+++ b/internal/exporter/exporter.go
@@ -1,39 +1,99 @@
package exporter
import (
- "adammathes.com/neko/models/feed"
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"html/template"
+
+ "adammathes.com/neko/models/feed"
)
+type OPML struct {
+ XMLName xml.Name `xml:"opml"`
+ Version string `xml:"version,attr"`
+ Head struct {
+ Title string `xml:"title"`
+ } `xml:"head"`
+ Body struct {
+ Outlines []Outline `xml:"outline"`
+ } `xml:"body"`
+}
+
+type Outline struct {
+ XMLName xml.Name `xml:"outline"`
+ Text string `xml:"text,attr"`
+ Title string `xml:"title,attr,omitempty"`
+ Type string `xml:"type,attr,omitempty"`
+ XMLURL string `xml:"xmlUrl,attr,omitempty"`
+ HTMLURL string `xml:"htmlUrl,attr,omitempty"`
+ Outlines []Outline `xml:"outline,omitempty"`
+}
+
func ExportFeeds(format string) string {
feeds, err := feed.All()
if err != nil {
- panic(err)
+ return ""
}
- s := ""
switch format {
case "text":
+ var b bytes.Buffer
for _, f := range feeds {
- s = s + fmt.Sprintf("%s\n", f.Url)
+ fmt.Fprintf(&b, "%s\n", f.Url)
}
+ return b.String()
case "opml":
- s = s + fmt.Sprintf(`<opml version="2.0"><head><title>neko feeds</title></head><body>`)
- s = s + fmt.Sprintf("\n")
+ var o OPML
+ o.Version = "2.0"
+ o.Head.Title = "neko feeds"
+
+ // Group by category
+ cats := make(map[string][]*feed.Feed)
+ var noCat []*feed.Feed
for _, f := range feeds {
- b, _ := xml.Marshal(f)
- s = s + fmt.Sprintf("%s\n", string(b))
+ if f.Category != "" {
+ cats[f.Category] = append(cats[f.Category], f)
+ } else {
+ noCat = append(noCat, f)
+ }
}
- s = s + fmt.Sprintf(`</body></opml>`)
+
+ for cat, fds := range cats {
+ out := Outline{Text: cat}
+ for _, f := range fds {
+ out.Outlines = append(out.Outlines, Outline{
+ Text: f.Title,
+ Title: f.Title,
+ Type: "rss",
+ XMLURL: f.Url,
+ HTMLURL: f.WebUrl,
+ })
+ }
+ o.Body.Outlines = append(o.Body.Outlines, out)
+ }
+
+ for _, f := range noCat {
+ o.Body.Outlines = append(o.Body.Outlines, Outline{
+ Text: f.Title,
+ Title: f.Title,
+ Type: "rss",
+ XMLURL: f.Url,
+ HTMLURL: f.WebUrl,
+ })
+ }
+
+ b, err := xml.MarshalIndent(o, "", " ")
+ if err != nil {
+ return ""
+ }
+ return xml.Header + string(b)
case "json":
- js, _ := json.Marshal(feeds)
- s = fmt.Sprintf("%s\n", js)
+ js, _ := json.MarshalIndent(feeds, "", " ")
+ return string(js)
case "html":
htmlTemplateString := `<html>
@@ -50,12 +110,15 @@ func ExportFeeds(format string) string {
</html>`
var bts bytes.Buffer
htmlTemplate, err := template.New("feeds").Parse(htmlTemplateString)
+ if err != nil {
+ return ""
+ }
err = htmlTemplate.Execute(&bts, feeds)
if err != nil {
- panic(err)
+ return ""
}
- s = bts.String()
+ return bts.String()
}
- return s
+ return ""
}