feat: implements sync command
This commit is contained in:
parent
a77d4f9604
commit
5a4a4d387a
4 changed files with 197 additions and 10 deletions
|
|
@ -12,6 +12,10 @@ type Article struct {
|
|||
FeedId int64
|
||||
}
|
||||
|
||||
type InsertMultipleOptions struct {
|
||||
IgnoreDuplicates bool
|
||||
}
|
||||
|
||||
func (a Article) Insert() (int64, error) {
|
||||
result, err := db.Exec("INSERT INTO article (name, url, readAt, feedId) VALUES (?, ?, ?, ?)", a.Name, a.Url, a.ReadAt, a.FeedId)
|
||||
|
||||
|
|
@ -43,6 +47,14 @@ func (a Article) MarkAsRead() error {
|
|||
}
|
||||
|
||||
func InsertMultipleArticles(articles []Article) error {
|
||||
return insertMultipleArticlesWithOpts(articles, InsertMultipleOptions{IgnoreDuplicates: false})
|
||||
}
|
||||
|
||||
func InsertIgnoreMultipleArticles(articles []Article) error {
|
||||
return insertMultipleArticlesWithOpts(articles, InsertMultipleOptions{IgnoreDuplicates: true})
|
||||
}
|
||||
|
||||
func insertMultipleArticlesWithOpts(articles []Article, opts InsertMultipleOptions) error {
|
||||
if len(articles) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -55,7 +67,15 @@ func InsertMultipleArticles(articles []Article) error {
|
|||
|
||||
defer tx.Rollback()
|
||||
|
||||
stmt, err := tx.Prepare("INSERT INTO article (name, url, readAt, feedId) VALUES (?, ?, ?, ?)")
|
||||
insertStmt := "INSERT "
|
||||
|
||||
if opts.IgnoreDuplicates {
|
||||
insertStmt = insertStmt + "OR IGNORE "
|
||||
}
|
||||
|
||||
finalStmt := insertStmt + "INTO article (name, url, readAt, feedId) VALUES (?, ?, ?, ?)"
|
||||
|
||||
stmt, err := tx.Prepare(finalStmt)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -35,8 +35,36 @@ func (f Feed) Insert() (int64, error) {
|
|||
return id, nil
|
||||
}
|
||||
|
||||
// This might be a problem if we have a LOT of feeds configured
|
||||
// as of now this works, so it is not at the top of the priority
|
||||
// list
|
||||
func FindAllFeeds() (*[]Feed, error) {
|
||||
rows, err := db.Query("SELECT id, name, url, createdAt, lastSyncedAt FROM feed")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var feeds []Feed
|
||||
|
||||
for rows.Next() {
|
||||
var feed Feed
|
||||
err := rows.Scan(&feed.ID, &feed.Name, &feed.Url, &feed.CreatedAt, &feed.LastSyncedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
feeds = append(feeds, feed)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &feeds, nil
|
||||
}
|
||||
|
||||
func FindAllFeedsWithArticleCount() (*[]FeedWithArticleCount, error) {
|
||||
rows, err := db.Query("SELECT f.id, f.name, f.url, f.createdAt, f.lastSyncedAt, COUNT(a.id) FROM feed as f LEFT JOIN article as a ON a.feedId = f.id;")
|
||||
rows, err := db.Query("SELECT f.id, f.name, f.url, f.createdAt, f.lastSyncedAt, COUNT(a.id) FROM feed as f LEFT JOIN article as a ON a.feedId = f.id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
101
internal/feed.go
101
internal/feed.go
|
|
@ -5,6 +5,8 @@ import (
|
|||
"freed/internal/database"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mmcdole/gofeed"
|
||||
"github.com/pterm/pterm"
|
||||
|
|
@ -31,18 +33,21 @@ func AddFeed(feedUrl string) (string, int, error) {
|
|||
return "", 0, err
|
||||
}
|
||||
|
||||
// TODO: Make the amount of articles configurable
|
||||
articles := make([]database.Article, 0, 10)
|
||||
|
||||
for i, v := range feed.Items {
|
||||
if i < 10 {
|
||||
a := database.Article{
|
||||
Name: v.Title,
|
||||
Url: v.Link,
|
||||
FeedId: feedId,
|
||||
}
|
||||
|
||||
articles = append(articles, a)
|
||||
if i > 10 {
|
||||
continue
|
||||
}
|
||||
|
||||
a := database.Article{
|
||||
Name: v.Title,
|
||||
Url: v.Link,
|
||||
FeedId: feedId,
|
||||
}
|
||||
|
||||
articles = append(articles, a)
|
||||
}
|
||||
|
||||
if err := database.InsertMultipleArticles(articles); err != nil {
|
||||
|
|
@ -80,6 +85,86 @@ func GetAllFeedsAsTable() (pterm.TableData, error) {
|
|||
return tableData, nil
|
||||
}
|
||||
|
||||
func SyncFeeds() error {
|
||||
now := time.Now()
|
||||
startOfToday := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
feeds, err := database.FindAllFeeds()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var syncWg sync.WaitGroup
|
||||
errChannel := make(chan error, len(*feeds))
|
||||
|
||||
for _, feed := range *feeds {
|
||||
syncWg.Add(1)
|
||||
go syncFeed(feed, startOfToday, &syncWg, errChannel)
|
||||
}
|
||||
|
||||
syncWg.Wait()
|
||||
close(errChannel)
|
||||
|
||||
var errors []error
|
||||
|
||||
for err := range errChannel {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
compositeErrMsg := fmt.Sprintf("Encountered %d errors:\n", len(errors))
|
||||
|
||||
for _, err := range errors {
|
||||
compositeErrMsg = compositeErrMsg + " " + fmt.Sprintf("- %v\n", err)
|
||||
}
|
||||
|
||||
return fmt.Errorf(compositeErrMsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncFeed(
|
||||
feed database.Feed,
|
||||
syncBefore time.Time,
|
||||
wg *sync.WaitGroup,
|
||||
errorChannel chan<- error,
|
||||
) {
|
||||
defer wg.Done()
|
||||
|
||||
if feed.LastSyncedAt.After(syncBefore) {
|
||||
return
|
||||
}
|
||||
|
||||
gFeed, err := parseByUrl(feed.Url)
|
||||
|
||||
if err != nil {
|
||||
errorChannel <- err
|
||||
return
|
||||
}
|
||||
|
||||
var articles []database.Article
|
||||
|
||||
for _, item := range gFeed.Items {
|
||||
if item.PublishedParsed.Before(*feed.LastSyncedAt) {
|
||||
continue
|
||||
}
|
||||
|
||||
article := database.Article{
|
||||
Name: item.Title,
|
||||
Url: item.Link,
|
||||
FeedId: feed.ID,
|
||||
}
|
||||
|
||||
articles = append(articles, article)
|
||||
}
|
||||
|
||||
if err := database.InsertIgnoreMultipleArticles(articles); err != nil {
|
||||
errorChannel <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func parseByUrl(u string) (*gofeed.Feed, error) {
|
||||
fp := gofeed.NewParser()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue