M db/db.go => db/db.go +157 -7
@@ 96,6 96,23 @@ func ReadDB(path string, gmniPath string) (*Storage, error) {
return s, nil
}
+func (s *Storage) deleteDBs(db *bolt.DB) error {
+ if err := s.Favorites.del(db); err != nil {
+ return err
+ }
+ if err := s.Queue.del(db); err != nil {
+ return err
+ }
+ if err := s.Items.del(db); err != nil {
+ return err
+ }
+ if err := s.Feeds.del(db); err != nil {
+ return err
+ }
+
+ return nil
+}
+
// Import will use an OPML file to insert feeds that you are not already
// subscribed.
func (s *Storage) Import(path string) error {
@@ 169,7 186,7 @@ func (s *Storage) Backup(path string) error {
m := barefeed.MessageV1{
Created: barefeed.ToTimestamp(time.Now()),
Generator: fmt.Sprintf("beagles v%s", s.Version),
- Feeds: make([]barefeed.Feed, len(s.Feeds)),
+ Feeds: make([]barefeed.FeedV1, len(s.Feeds)),
}
i := 0
@@ 179,12 196,12 @@ func (s *Storage) Backup(path string) error {
return err
}
- f := barefeed.Feed{
+ f := barefeed.FeedV1{
Feed: k,
Title: v.Title,
Description: d,
Link: v.Link,
- Items: make([]barefeed.Item, len(v.Items)),
+ Items: make([]barefeed.ItemV1, len(v.Items)),
}
if v.Updated != nil {
@@ 203,7 220,7 @@ func (s *Storage) Backup(path string) error {
return nil
}
- it := barefeed.Item{
+ it := barefeed.ItemV1{
Link: k1,
Title: val.Title,
Content: c,
@@ 213,7 230,7 @@ func (s *Storage) Backup(path string) error {
}
if val.Type != "" || val.Location != "" || val.Length != "" {
- it.Media = &barefeed.Media{
+ it.Media = &barefeed.MediaV1{
Location: val.Location,
Mimetype: val.Type,
Position: int64(val.PlaybackPOS),
@@ 239,8 256,141 @@ func (s *Storage) Backup(path string) error {
// Restore will clear the database and restore using a barefeed file
func (s *Storage) Restore(path string) error {
- // TODOL Implement me.
- return fmt.Errorf("restore is not yet implemented")
+ db, err := openDB(s.Path)
+ if err != nil {
+ return err
+ }
+
+ // delete all db buckets
+ if err := s.deleteDBs(db); err != nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ }
+
+ // recreate all buckets
+ if err := s.Favorites.create(db); err != nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ }
+ if err := s.Queue.create(db); err != nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ }
+ if err := s.Items.create(db); err != nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ }
+ if err := s.Feeds.create(db); err != nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ }
+
+ msg, err := barefeed.FromFile(filepath.Clean(path))
+ if err != nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ } else if msg == nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return fmt.Errorf("message is empty")
+ }
+
+ switch m := (*msg).(type) {
+ case barefeed.MessageV1:
+ if err := s.processMessageV1(db, m); err != nil {
+ if err := s.deleteDBs(db); err != nil {
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ }
+
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return err
+ }
+ default:
+ if err := db.Close(); err != nil {
+ return err
+ }
+ return fmt.Errorf("unsupported message format")
+ }
+
+ return db.Close()
+}
+
+func (s *Storage) processMessageV1(db *bolt.DB, msg barefeed.MessageV1) error {
+ for _, feed := range msg.Feeds {
+ f := Feed{
+ Title: feed.Title,
+ Description: feed.Description,
+ UpdateURL: feed.Feed,
+ Link: feed.Link,
+ Items: make(map[string]bool),
+ }
+
+ if feed.Updated != nil {
+ up := (*feed.Updated).Time()
+ f.Updated = &up
+ }
+
+ for _, item := range feed.Items {
+ it := Item{
+ FeedURL: f.UpdateURL,
+ Title: item.Title,
+ Content: item.Content,
+ Link: item.Link,
+ Date: item.Date.Time(),
+ Read: item.Read,
+ Favorite: item.Favorite,
+ }
+
+ if item.Media != nil {
+ it.Type = item.Media.Mimetype
+ it.Location = item.Media.Location
+ it.Length = fmt.Sprintf("%d", item.Media.Length)
+ it.PlaybackPOS = int(item.Media.Position)
+ }
+
+ if !it.Read {
+ if err := s.Queue.Insert(db, it.Link); err != nil {
+ return err
+ }
+ }
+
+ if it.Favorite {
+ if err := s.Favorites.Insert(db, it.Link); err != nil {
+ return err
+ }
+ }
+
+ f.Items[it.Link] = false
+ s.Items[it.Link] = &it
+ if _, err := s.Items.Insert(db, &it); err != nil {
+ return err
+ }
+ }
+
+ if err := s.Feeds.insert(db, &f); err != nil {
+ return err
+ }
+ }
+
+ return nil
}
// CreateFeed will collect the rss feed and process through the elements
M db/favorite.go => db/favorite.go +13 -0
@@ 40,6 40,19 @@ func (f Favorites) create(db *bolt.DB) error {
})
}
+func (f Favorites) del(db *bolt.DB) error {
+ return db.Update(func(tx *bolt.Tx) error {
+ if b := tx.Bucket([]byte(favoriteTbl)); b == nil {
+ return nil
+ }
+
+ if err := tx.DeleteBucket([]byte(favoriteTbl)); err != nil {
+ return fmt.Errorf("error deleting favorite bucket: %s", err.Error())
+ }
+ return nil
+ })
+}
+
// Read will load the contents of the database into this structure
func (f Favorites) Read(db *bolt.DB) error {
if err := f.create(db); err != nil {
M db/feed.go => db/feed.go +26 -5
@@ 134,6 134,19 @@ func (f Feeds) create(db *bolt.DB) error {
})
}
+func (f Feeds) del(db *bolt.DB) error {
+ return db.Update(func(tx *bolt.Tx) error {
+ if b := tx.Bucket([]byte(feedTbl)); b == nil {
+ return nil
+ }
+
+ if err := tx.DeleteBucket([]byte(feedTbl)); err != nil {
+ return fmt.Errorf("error deleting feed bucket: %s", err.Error())
+ }
+ return nil
+ })
+}
+
// Read will load the contents of the database into this structure
func (f Feeds) Read(db *bolt.DB) error {
if err := f.create(db); err != nil {
@@ 176,20 189,28 @@ func (f Feeds) Insert(db *bolt.DB, url string, gmniPath string) ([]*gofeed.Item,
feed.UpdateURL = url
+ if err := f.insert(db, feed); err != nil {
+ return nil, err
+ }
+
+ return pfeed.Items, nil
+}
+
+func (f Feeds) insert(db *bolt.DB, feed *Feed) error {
feedJSON, err := json.Marshal(feed)
if err != nil {
- return nil, fmt.Errorf("unable to marshal feed: %s", err.Error())
+ return fmt.Errorf("unable to marshal feed: %s", err.Error())
}
if err := db.Update(func(tx *bolt.Tx) error {
- return tx.Bucket([]byte(feedTbl)).Put([]byte(url), feedJSON)
+ return tx.Bucket([]byte(feedTbl)).Put([]byte(feed.UpdateURL), feedJSON)
}); err != nil {
- return nil, fmt.Errorf("unable to insert feed: %s", err.Error())
+ return fmt.Errorf("unable to insert feed: %s", err.Error())
}
- f[url] = feed
+ f[feed.UpdateURL] = feed
- return pfeed.Items, nil
+ return nil
}
// Delete will remove a feed from the database
M db/item.go => db/item.go +13 -0
@@ 182,6 182,19 @@ func (f Items) create(db *bolt.DB) error {
})
}
+func (f Items) del(db *bolt.DB) error {
+ return db.Update(func(tx *bolt.Tx) error {
+ if b := tx.Bucket([]byte(itemTbl)); b == nil {
+ return nil
+ }
+
+ if err := tx.DeleteBucket([]byte(itemTbl)); err != nil {
+ return fmt.Errorf("error deleting item bucket: %s", err.Error())
+ }
+ return nil
+ })
+}
+
// Read will load the contents of the database into this structure
func (f Items) Read(db *bolt.DB) error {
if err := f.create(db); err != nil {
M db/queue.go => db/queue.go +13 -0
@@ 40,6 40,19 @@ func (f Queue) create(db *bolt.DB) error {
})
}
+func (f Queue) del(db *bolt.DB) error {
+ return db.Update(func(tx *bolt.Tx) error {
+ if b := tx.Bucket([]byte(queueTbl)); b == nil {
+ return nil
+ }
+
+ if err := tx.DeleteBucket([]byte(queueTbl)); err != nil {
+ return fmt.Errorf("error deleting queue bucket: %s", err.Error())
+ }
+ return nil
+ })
+}
+
// Read will load the contents of the database into this structure
func (f Queue) Read(db *bolt.DB) error {
if err := f.create(db); err != nil {
M go.mod => go.mod +2 -2
@@ 4,8 4,8 @@ go 1.15
require (
git.sr.ht/~adnano/go-gemini v0.1.10
- git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201231052557-c9dc6d4deb60
- git.sr.ht/~chrisppy/go-opml v0.0.0-20201229021831-d223d86f160e
+ git.sr.ht/~chrisppy/go-barefeed v0.1.0
+ git.sr.ht/~chrisppy/go-opml v1.0.0
git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc
git.sr.ht/~sircmpwn/getopt v0.0.0-20201218204720-9961a9c6298f
github.com/DataDrake/waterlog v1.0.5
M go.sum => go.sum +4 -4
@@ 1,9 1,9 @@
git.sr.ht/~adnano/go-gemini v0.1.10 h1:enuYuY2pC+1BsP1GE73wLIyhXc4r+Ryx6TUubynmSgo=
git.sr.ht/~adnano/go-gemini v0.1.10/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3VJ5rnZWKq0=
-git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201231052557-c9dc6d4deb60 h1:B9GcMx6b4PirZp+kywPHnKwggOGKopWgwd+J4FvxXQg=
-git.sr.ht/~chrisppy/go-barefeed v0.0.0-20201231052557-c9dc6d4deb60/go.mod h1:V/4QgRdPISXxrbyn/0YNdrh1qR2PHk8hdcIJsdmJFlQ=
-git.sr.ht/~chrisppy/go-opml v0.0.0-20201229021831-d223d86f160e h1:XOqA6g2gAr/ma6/W8xlhyt7CUIhkSsi+vGBMpXGiaxk=
-git.sr.ht/~chrisppy/go-opml v0.0.0-20201229021831-d223d86f160e/go.mod h1:dhjKH7fks5UN50Cnma/JCtxx8IRw8xAicwKV9gAtsHk=
+git.sr.ht/~chrisppy/go-barefeed v0.1.0 h1:kyf1sJx4uGLesiXagd4sCP9C8+TPge+y/VxFTX6dI4E=
+git.sr.ht/~chrisppy/go-barefeed v0.1.0/go.mod h1:V/4QgRdPISXxrbyn/0YNdrh1qR2PHk8hdcIJsdmJFlQ=
+git.sr.ht/~chrisppy/go-opml v1.0.0 h1:Vf7UHZSXCiilzDBhfYT4RHo9ijmwiNkNxhXkyy3GLoM=
+git.sr.ht/~chrisppy/go-opml v1.0.0/go.mod h1:dhjKH7fks5UN50Cnma/JCtxx8IRw8xAicwKV9gAtsHk=
git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc h1:51BD67xFX+bozd3ZRuOUfalrhx4/nQSh6A9lI08rYOk=
git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc/go.mod h1:t+Ww6SR24yYnXzEWiNlOY0AFo5E9B73X++10lrSpp4U=
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
M ui/content.go => ui/content.go +2 -20
@@ 22,7 22,6 @@ import (
"time"
"git.sr.ht/~chrisppy/beagles/db"
- "git.sr.ht/~chrisppy/beagles/util"
"github.com/gdamore/tcell/v2"
tui "gitlab.com/tslocum/cview"
)
@@ 63,16 62,7 @@ func (w *content) setDescription(text string, updated *time.Time, isHTML bool) {
sb.WriteByte('\n')
}
- if isHTML {
- t, err := util.StripHTML(text)
- if err != nil {
- w.Widget.SetTextColor(w.ErrColor)
- t = err.Error()
- }
- sb.WriteString(t)
- } else {
- sb.WriteString(text)
- }
+ sb.WriteString(text)
w.Widget.SetText(sb.String())
w.Widget.ScrollToBeginning()
@@ 108,15 98,7 @@ func (w *content) setText(item *db.Item) {
if c == "" {
sb.WriteString("No Content")
sb.WriteByte('\n')
- } else if strings.HasPrefix(item.Link, "http") {
- t, err := util.StripHTML(c)
- if err != nil {
- w.Widget.SetTextColor(w.ErrColor)
- t = err.Error()
- }
- sb.WriteString(t)
- sb.WriteByte('\n')
- } else if strings.HasPrefix(item.Link, "gemini") {
+ } else {
sb.WriteString(c)
sb.WriteByte('\n')
}
M util/util.go => util/util.go +3 -2
@@ 297,8 297,9 @@ func ExtPlay(path, bin string, args []string) {
// StripHTML from the included text
func StripHTML(text string) (string, error) {
options := ht.Options{
- OmitLinks: false,
- PrettyTables: true,
+ OmitLinks: false,
+ PrettyTables: true,
+ PrettyTablesOptions: ht.NewPrettyTablesOptions(),
}
return ht.FromString(text, options)