166 lines
4.4 KiB
Go
166 lines
4.4 KiB
Go
// Copyright (C) 2021 nytpu
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
// For more license details, see LICENSE or <https://www.gnu.org/licenses/agpl-3.0.html>.
|
|
|
|
// This file incorporates code from makeworld's amfora:
|
|
// https://github.com/makeworld-the-better-one/amfora/blob/master/subscriptions/structs.go
|
|
// Original source is Copyright 2020 makeworld and is licensed under the
|
|
// terms of the GNU General Public License Version 3.0.
|
|
// https://www.gnu.org/licenses/gpl-3.0-standalone.html
|
|
|
|
package core
|
|
|
|
import (
|
|
"net/url"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/mmcdole/gofeed"
|
|
)
|
|
|
|
// FullData is used for both reading from json and for storing the global feeds
|
|
// map. contains read mutexes so concurrency can be used.
|
|
type FullData struct {
|
|
Feeds map[url.URL]*Feed `json:"feeds,omitempty"`
|
|
Pages map[url.URL]*Page `json:"pages,omitempty"`
|
|
|
|
FeedsMu *sync.RWMutex
|
|
PagesMu *sync.RWMutex
|
|
}
|
|
|
|
// Feed holds all the necessary fields when storing a feed
|
|
type Feed struct {
|
|
Title string `json:"title,omitempty"`
|
|
Link *url.URL `json:"link,omitempty"` // link to the human-readable page for the feed. same as FeedLink for gemini feeds.
|
|
FeedLink *url.URL `json:"feedlink,omitempty"` // link to the feed itself.
|
|
Updated time.Time `json:"updated,omitempty"`
|
|
Items []*Item `json:"items,omitempty"`
|
|
}
|
|
|
|
// Item is a single item from a subscripton
|
|
type Item struct {
|
|
Title string `json:"title,omitempty"` // if empty, use Link as title
|
|
Content string `json:"content,omitempty"`
|
|
Link *url.URL `json:"link,omitempty"`
|
|
Published time.Time `json:"updated,omitempty"`
|
|
}
|
|
|
|
// Page holds all the necessary fields when storing a page being watched for
|
|
// changes.
|
|
type Page struct {
|
|
Title string `json:"title,omitempty"`
|
|
Link *url.URL `json:"link,omitempty"`
|
|
Updated time.Time `json:"updated,omitempty"`
|
|
Hash string `json:"hash,omitempty"`
|
|
}
|
|
|
|
// gofeedConvert converts a gofeed.Feed into a Feed struct
|
|
func GofeedConvert(f *gofeed.Feed, title string) (*Feed, error) {
|
|
new := &Feed{}
|
|
|
|
if title != "" {
|
|
new.Title = title
|
|
} else {
|
|
new.Title = f.Title
|
|
}
|
|
new.Updated = *f.UpdatedParsed
|
|
var err error
|
|
new.Link, err = url.ParseRequestURI(f.Link)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
new.FeedLink, err = url.ParseRequestURI(f.FeedLink)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, v := range f.Items {
|
|
item := &Item{}
|
|
item.Title = v.Title
|
|
item.Content = v.Content
|
|
item.Published = *v.PublishedParsed
|
|
item.Link, err = url.ParseRequestURI(f.FeedLink)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
new.Items = append(new.Items, item)
|
|
}
|
|
|
|
return new, nil
|
|
}
|
|
|
|
// Implement sort.Interface for Feed
|
|
|
|
// Len returns the length of Items.
|
|
func (f *Feed) Len() int {
|
|
return len(f.Items)
|
|
}
|
|
|
|
// Less compares Published of Items[i], Items[k] and returns true if Items[i] is
|
|
// before Items[j].
|
|
func (f *Feed) Less(i, j int) bool {
|
|
return f.Items[i].Published.Before(f.Items[j].Published)
|
|
}
|
|
|
|
// Swap swaps Items[i] and Items[j].
|
|
func (f *Feed) Swap(i, j int) {
|
|
f.Items[i], f.Items[j] = f.Items[j], f.Items[i]
|
|
}
|
|
|
|
// Lock locks both feed and page mutexes.
|
|
func (j *FullData) Lock() {
|
|
j.FeedsMu.Lock()
|
|
j.PagesMu.Lock()
|
|
}
|
|
|
|
// Unlock unlocks both feed and page mutexes.
|
|
func (j *FullData) Unlock() {
|
|
j.FeedsMu.Unlock()
|
|
j.PagesMu.Unlock()
|
|
}
|
|
|
|
// RLock read-locks both feed and page mutexes.
|
|
func (j *FullData) RLock() {
|
|
j.FeedsMu.RLock()
|
|
j.PagesMu.RLock()
|
|
}
|
|
|
|
// RUnlock read-unlocks both feed and page mutexes.
|
|
func (j *FullData) RUnlock() {
|
|
j.FeedsMu.RUnlock()
|
|
j.PagesMu.RUnlock()
|
|
}
|
|
|
|
// PageEntry is a single item on a subscriptions page. It is used for both feeds
|
|
// and pages.
|
|
type PageEntry struct {
|
|
Prefix string // whatever goes before the post title to identify the feed.
|
|
Title string // article/entry title
|
|
URL string
|
|
Published time.Time
|
|
}
|
|
|
|
// PageEntries is reverse chronological list of entries used when generating subs.
|
|
type PageEntries struct {
|
|
Entries []*PageEntry
|
|
}
|
|
|
|
// Implement sort.Interface for PageEntries
|
|
|
|
// Len returns the length of Entries
|
|
func (e *PageEntries) Len() int {
|
|
return len(e.Entries)
|
|
}
|
|
|
|
// Less compares the Published if Entries[i], Entries[j] and returns true if
|
|
// Entries[i] is /after/ Entries[j] (for reverse chronological sorting)
|
|
func (e *PageEntries) Less(i, j int) bool {
|
|
return e.Entries[i].Published.After(e.Entries[j].Published)
|
|
}
|
|
|
|
// Swap swaps Entries[i] and Entries[j]
|
|
func (e *PageEntries) Swap(i, j int) {
|
|
e.Entries[i], e.Entries[j] = e.Entries[j], e.Entries[i]
|
|
}
|