aboutsummaryrefslogtreecommitdiffstats
path: root/internal/importer/importer.go
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/importer/importer.go
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/importer/importer.go')
-rw-r--r--internal/importer/importer.go113
1 files changed, 105 insertions, 8 deletions
diff --git a/internal/importer/importer.go b/internal/importer/importer.go
index 73a2cd8..f74ace1 100644
--- a/internal/importer/importer.go
+++ b/internal/importer/importer.go
@@ -1,12 +1,14 @@
package importer
import (
- // "bufio"
+ "bufio"
"encoding/json"
- //"fmt"
+ "encoding/xml"
+ "errors"
"io"
"log"
"os"
+ "strings"
"adammathes.com/neko/models/feed"
"adammathes.com/neko/models/item"
@@ -32,15 +34,101 @@ type IDate struct {
Date string `json:"$date"`
}
-func ImportJSON(filename string) error {
+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"`
+}
- f, err := os.Open(filename)
- if err != nil {
+type Outline struct {
+ Text string `xml:"text,attr"`
+ Title string `xml:"title,attr"`
+ Type string `xml:"type,attr"`
+ XMLURL string `xml:"xmlUrl,attr"`
+ HTMLURL string `xml:"htmlUrl,attr"`
+ Category string `xml:"category,attr"`
+ Outlines []Outline `xml:"outline"`
+}
+
+func ImportFeeds(format string, r io.Reader) error {
+ switch format {
+ case "opml":
+ return ImportOPML(r)
+ case "text":
+ return ImportText(r)
+ case "json":
+ return ImportJSONReader(r)
+ default:
+ return errors.New("unsupported import format")
+ }
+}
+
+func ImportOPML(r io.Reader) error {
+ var o OPML
+ if err := xml.NewDecoder(r).Decode(&o); err != nil {
return err
}
- defer f.Close()
- dec := json.NewDecoder(f)
+ var walk func([]Outline, string)
+ walk = func(outlines []Outline, cat string) {
+ for _, out := range outlines {
+ if out.Type == "rss" || out.XMLURL != "" {
+ f := &feed.Feed{
+ Url: out.XMLURL,
+ Title: out.Title,
+ WebUrl: out.HTMLURL,
+ Category: cat,
+ }
+ if f.Title == "" {
+ f.Title = out.Text
+ }
+ if f.Category == "" {
+ f.Category = out.Category
+ }
+ err := f.Create()
+ if err != nil {
+ log.Printf("error importing %s: %v", f.Url, err)
+ } else {
+ log.Printf("imported %s", f.Url)
+ }
+ }
+ if len(out.Outlines) > 0 {
+ newCat := cat
+ if out.XMLURL == "" && out.Text != "" {
+ newCat = out.Text
+ }
+ walk(out.Outlines, newCat)
+ }
+ }
+ }
+ walk(o.Body.Outlines, "")
+ return nil
+}
+
+func ImportText(r io.Reader) error {
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ err := feed.NewFeed(line)
+ if err != nil {
+ log.Printf("error importing %s: %v", line, err)
+ } else {
+ log.Printf("imported %s", line)
+ }
+ }
+ return scanner.Err()
+}
+
+func ImportJSONReader(r io.Reader) error {
+ dec := json.NewDecoder(r)
for {
var ii IItem
if err := dec.Decode(&ii); err == io.EOF {
@@ -57,6 +145,15 @@ func ImportJSON(filename string) error {
return nil
}
+func ImportJSON(filename string) error {
+ f, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return ImportJSONReader(f)
+}
+
func InsertIItem(ii *IItem) error {
var f feed.Feed
@@ -67,6 +164,7 @@ func InsertIItem(ii *IItem) error {
if err != nil {
f.Url = ii.Feed.Url
f.Title = ii.Feed.Title
+ f.WebUrl = ii.Feed.WebUrl
err = f.Create()
if err != nil {
return err
@@ -84,6 +182,5 @@ func InsertIItem(ii *IItem) error {
}
err = i.Create()
- log.Printf("inserted %s\n", i.Url)
return err
}