miniflux-v2/internal/reader/rdf/adapter.go

115 lines
2.8 KiB
Go

// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package rdf // import "miniflux.app/v2/internal/reader/rdf"
import (
"html"
"log/slog"
"strings"
"time"
"miniflux.app/v2/internal/crypto"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/reader/date"
"miniflux.app/v2/internal/reader/sanitizer"
"miniflux.app/v2/internal/urllib"
)
type RDFAdapter struct {
rdf *RDF
}
func NewRDFAdapter(rdf *RDF) *RDFAdapter {
return &RDFAdapter{rdf}
}
func (r *RDFAdapter) BuildFeed(baseURL string) *model.Feed {
feed := &model.Feed{
Title: stripTags(r.rdf.Channel.Title),
FeedURL: strings.TrimSpace(baseURL),
SiteURL: strings.TrimSpace(r.rdf.Channel.Link),
}
if feed.Title == "" {
feed.Title = baseURL
}
if siteURL, err := urllib.AbsoluteURL(feed.FeedURL, feed.SiteURL); err == nil {
feed.SiteURL = siteURL
}
for _, item := range r.rdf.Items {
entry := model.NewEntry()
itemLink := strings.TrimSpace(item.Link)
// Populate the entry URL.
if itemLink == "" {
entry.URL = feed.SiteURL // Fallback to the feed URL if the entry URL is empty.
} else if entryURL, err := urllib.AbsoluteURL(feed.SiteURL, itemLink); err == nil {
entry.URL = entryURL
} else {
entry.URL = itemLink
}
// Populate the entry title.
for _, title := range []string{item.Title, item.DublinCoreTitle} {
title = strings.TrimSpace(title)
if title != "" {
entry.Title = html.UnescapeString(title)
break
}
}
// If the entry title is empty, we use the entry URL as a fallback.
if entry.Title == "" {
entry.Title = entry.URL
}
// Populate the entry content.
if item.DublinCoreContent != "" {
entry.Content = item.DublinCoreContent
} else {
entry.Content = item.Description
}
// Generate the entry hash.
hashValue := itemLink
if hashValue == "" {
hashValue = item.Title + item.Description // Fallback to the title and description if the link is empty.
}
entry.Hash = crypto.Hash(hashValue)
// Populate the entry date.
entry.Date = time.Now()
if item.DublinCoreDate != "" {
if itemDate, err := date.Parse(item.DublinCoreDate); err != nil {
slog.Debug("Unable to parse date from RDF feed",
slog.String("date", item.DublinCoreDate),
slog.String("link", itemLink),
slog.Any("error", err),
)
} else {
entry.Date = itemDate
}
}
// Populate the entry author.
switch {
case item.DublinCoreCreator != "":
entry.Author = stripTags(item.DublinCoreCreator)
case r.rdf.Channel.DublinCoreCreator != "":
entry.Author = stripTags(r.rdf.Channel.DublinCoreCreator)
}
feed.Entries = append(feed.Entries, entry)
}
return feed
}
func stripTags(value string) string {
return strings.TrimSpace(sanitizer.StripTags(value))
}