atom_03_adapter.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. // TODO No need for a constructor, as it's only used in this package
  17. func NewAtom03Adapter(atomFeed *atom03Feed) *atom03Adapter {
  18. return &atom03Adapter{atomFeed}
  19. }
  20. func (a *atom03Adapter) buildFeed(baseURL string) *model.Feed {
  21. feed := new(model.Feed)
  22. // Populate the feed URL.
  23. feedURL := a.atomFeed.Links.firstLinkWithRelation("self")
  24. if feedURL != "" {
  25. if absoluteFeedURL, err := urllib.AbsoluteURL(baseURL, feedURL); err == nil {
  26. feed.FeedURL = absoluteFeedURL
  27. }
  28. } else {
  29. feed.FeedURL = baseURL
  30. }
  31. // Populate the site URL.
  32. siteURL := a.atomFeed.Links.originalLink()
  33. if siteURL != "" {
  34. if absoluteSiteURL, err := urllib.AbsoluteURL(baseURL, siteURL); err == nil {
  35. feed.SiteURL = absoluteSiteURL
  36. }
  37. } else {
  38. feed.SiteURL = baseURL
  39. }
  40. // Populate the feed title.
  41. feed.Title = a.atomFeed.Title.content()
  42. if feed.Title == "" {
  43. feed.Title = feed.SiteURL
  44. }
  45. for _, atomEntry := range a.atomFeed.Entries {
  46. entry := model.NewEntry()
  47. // Populate the entry URL.
  48. entry.URL = atomEntry.Links.originalLink()
  49. if entry.URL != "" {
  50. if absoluteEntryURL, err := urllib.AbsoluteURL(feed.SiteURL, entry.URL); err == nil {
  51. entry.URL = absoluteEntryURL
  52. }
  53. }
  54. // Populate the entry content.
  55. entry.Content = atomEntry.Content.content()
  56. if entry.Content == "" {
  57. entry.Content = atomEntry.Summary.content()
  58. }
  59. // Populate the entry title.
  60. entry.Title = atomEntry.Title.content()
  61. if entry.Title == "" {
  62. entry.Title = sanitizer.TruncateHTML(entry.Content, 100)
  63. }
  64. if entry.Title == "" {
  65. entry.Title = entry.URL
  66. }
  67. // Populate the entry author.
  68. entry.Author = atomEntry.Author.PersonName()
  69. if entry.Author == "" {
  70. entry.Author = a.atomFeed.Author.PersonName()
  71. }
  72. // Populate the entry date.
  73. for _, value := range []string{atomEntry.Issued, atomEntry.Modified, atomEntry.Created} {
  74. if parsedDate, err := date.Parse(value); err == nil {
  75. entry.Date = parsedDate
  76. break
  77. } else {
  78. slog.Debug("Unable to parse date from Atom 0.3 feed",
  79. slog.String("date", value),
  80. slog.String("id", atomEntry.ID),
  81. slog.Any("error", err),
  82. )
  83. }
  84. }
  85. if entry.Date.IsZero() {
  86. entry.Date = time.Now()
  87. }
  88. // Generate the entry hash.
  89. for _, value := range []string{atomEntry.ID, atomEntry.Links.originalLink()} {
  90. if value != "" {
  91. entry.Hash = crypto.SHA256(value)
  92. break
  93. }
  94. }
  95. feed.Entries = append(feed.Entries, entry)
  96. }
  97. return feed
  98. }