From 6fa13a06411048f3217397f4285b3e64e7b9ee58 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Sat, 14 Feb 2026 09:42:14 -0800 Subject: feature: implement full OPML and Text import/export (fixing NK-r6nhj0) --- internal/importer/importer.go | 113 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 8 deletions(-) (limited to 'internal/importer/importer.go') 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 } -- cgit v1.2.3