Well, silly me. I forgot to backup the postgres for miniflux, and the miniflux is gone for me.
On the good side, I backup the feeds.opml
every day, so I just need to migrate to something
else. Now that I move to thunderbird as my default mail/nntp reader, why not just add some RSS too.
To my surprise, thunderbird understands opml file a bit different. When you add a feed manually, you will get:
<outline title="same as feed">
<outline title="same as feed" text="same as title usually" xmlUrl="https://url-to-rss-or-atom" htmlUrl="https://url-to-website" type="rss" version="RSS"/>
</outline>
The wrapping outline is required, otherwise, you cannot see each feed individually.
Moreover, type
and version
are required too, otherwise, thunderbird treats this
outline as a simple folder. (fz:
options are private to thunderbird, they are optional for new feeds)
Well, let’s roll out a short script to fix that:
// PUBLIC DOMAIN
package main
import (
"os"
"github.com/beevik/etree"
)
func newDoc() (chan *etree.Element, chan struct{}) {
input := make(chan *etree.Element, 1)
done := make(chan struct{})
go func() {
doc := etree.NewDocument()
doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`)
doc.Indent(2)
opml := doc.CreateElement("opml")
opml.CreateAttr("version", "2.0")
root := opml.CreateElement("body").CreateElement("outline")
root.CreateAttr("title", "All")
for {
lastSeen := false
select {
case elem := <-input:
if elem == nil {
doc.WriteTo(os.Stdout)
lastSeen = true
break
}
folder := root.CreateElement("outline")
folder.CreateAttr("title", elem.SelectAttrValue("title", ""))
feed := elem.Copy()
feed.CreateAttr("version", "RSS")
feed.CreateAttr("type", "rss")
//feed.CreateAttr("fz:quickMode", "false")
//feed.CreateAttr("fz:options", `{"version":2,"updates":{"enabled":true,"updateMinutes":1440,"updateUnits":"min"},"category":{"enabled":false,"prefixEnabled":false,"prefix":""}}`)
folder.AddChild(feed)
}
if lastSeen {
break
}
}
close(done)
}()
return input, done
}
func main() {
output, done := newDoc()
doc := etree.NewDocument()
if err := doc.ReadFromFile(os.Args[1]); err != nil {
panic(err)
}
root := doc.SelectElement("opml").SelectElement("body")
for _, elem := range root.SelectElements("outline") {
output <- elem
}
output <- nil
<-done
}