atom_03_adapter.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package atom // import "miniflux.app/v2/internal/reader/atom"
  4. import (
  5. "log/slog"
  6. "time"
  7. "miniflux.app/v2/internal/crypto"
  8. "miniflux.app/v2/internal/model"
  9. "miniflux.app/v2/internal/reader/date"
  10. "miniflux.app/v2/internal/reader/sanitizer"
  11. "miniflux.app/v2/internal/urllib"
  12. )
  13. type Atom03Adapter struct {
  14. atomFeed *Atom03Feed
  15. }
  16. func NewAtom03Adapter(atomFeed *Atom03Feed) *Atom03Adapter {
  17. return &Atom03Adapter{atomFeed}
  18. }
  19. func (a *Atom03Adapter) BuildFeed(baseURL string) *model.Feed {
  20. feed := new(model.Feed)
  21. // Populate the feed URL.
  22. feedURL := a.atomFeed.Links.firstLinkWithRelation("self")
  23. if feedURL != "" {
  24. if absoluteFeedURL, err := urllib.AbsoluteURL(baseURL, feedURL); err == nil {
  25. feed.FeedURL = absoluteFeedURL
  26. }
  27. } else {
  28. feed.FeedURL = baseURL
  29. }
  30. // Populate the site URL.
  31. siteURL := a.atomFeed.Links.OriginalLink()
  32. if siteURL != "" {
  33. if absoluteSiteURL, err := urllib.AbsoluteURL(baseURL, siteURL); err == nil {
  34. feed.SiteURL = absoluteSiteURL
  35. }
  36. } else {
  37. feed.SiteURL = baseURL
  38. }
  39. // Populate the feed title.
  40. feed.Title = a.atomFeed.Title.Content()
  41. if feed.Title == "" {
  42. feed.Title = feed.SiteURL
  43. }
  44. for _, atomEntry := range a.atomFeed.Entries {
  45. entry := model.NewEntry()
  46. // Populate the entry URL.
  47. entry.URL = atomEntry.Links.OriginalLink()
  48. if entry.URL != "" {
  49. if absoluteEntryURL, err := urllib.AbsoluteURL(feed.SiteURL, entry.URL); err == nil {
  50. entry.URL = absoluteEntryURL
  51. }
  52. }
  53. // Populate the entry content.
  54. entry.Content = atomEntry.Content.Content()
  55. if entry.Content == "" {
  56. entry.Content = atomEntry.Summary.Content()
  57. }
  58. // Populate the entry title.
  59. entry.Title = atomEntry.Title.Content()
  60. if entry.Title == "" {
  61. entry.Title = sanitizer.TruncateHTML(entry.Content, 100)
  62. }
  63. if entry.Title == "" {
  64. entry.Title = entry.URL
  65. }
  66. // Populate the entry author.
  67. entry.Author = atomEntry.Author.PersonName()
  68. if entry.Author == "" {
  69. entry.Author = a.atomFeed.Author.PersonName()
  70. }
  71. // Populate the entry date.
  72. for _, value := range []string{atomEntry.Issued, atomEntry.Modified, atomEntry.Created} {
  73. if parsedDate, err := date.Parse(value); err == nil {
  74. entry.Date = parsedDate
  75. break
  76. } else {
  77. slog.Debug("Unable to parse date from Atom 0.3 feed",
  78. slog.String("date", value),
  79. slog.String("id", atomEntry.ID),
  80. slog.Any("error", err),
  81. )
  82. }
  83. }
  84. if entry.Date.IsZero() {
  85. entry.Date = time.Now()
  86. }
  87. // Generate the entry hash.
  88. for _, value := range []string{atomEntry.ID, atomEntry.Links.OriginalLink()} {
  89. if value != "" {
  90. entry.Hash = crypto.SHA256(value)
  91. break
  92. }
  93. }
  94. feed.Entries = append(feed.Entries, entry)
  95. }
  96. return feed
  97. }