atom_03_adapter.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. "strings"
  7. "time"
  8. "miniflux.app/v2/internal/crypto"
  9. "miniflux.app/v2/internal/model"
  10. "miniflux.app/v2/internal/reader/date"
  11. "miniflux.app/v2/internal/reader/sanitizer"
  12. "miniflux.app/v2/internal/urllib"
  13. )
  14. type atom03Adapter struct {
  15. atomFeed *atom03Feed
  16. }
  17. func (a *atom03Adapter) buildFeed(baseURL string) *model.Feed {
  18. feed := &model.Feed{
  19. FeedURL: baseURL,
  20. SiteURL: baseURL,
  21. }
  22. // Populate the feed URL.
  23. feedURL := a.atomFeed.Links.firstLinkWithRelation("self")
  24. if feedURL != "" {
  25. if absoluteFeedURL, err := urllib.ResolveToAbsoluteURL(baseURL, feedURL); err == nil {
  26. feed.FeedURL = absoluteFeedURL
  27. }
  28. }
  29. // Populate the site URL.
  30. siteURL := a.atomFeed.Links.originalLink()
  31. if siteURL != "" {
  32. if absoluteSiteURL, err := urllib.ResolveToAbsoluteURL(baseURL, siteURL); err == nil {
  33. feed.SiteURL = absoluteSiteURL
  34. }
  35. }
  36. // Populate the feed title.
  37. feed.Title = a.atomFeed.Title.content()
  38. if feed.Title == "" {
  39. feed.Title = feed.SiteURL
  40. }
  41. for _, atomEntry := range a.atomFeed.Entries {
  42. entry := model.NewEntry()
  43. // Populate the entry URL.
  44. entry.URL = atomEntry.Links.originalLink()
  45. if entry.URL != "" {
  46. if absoluteEntryURL, err := urllib.ResolveToAbsoluteURL(feed.SiteURL, entry.URL); err == nil {
  47. entry.URL = absoluteEntryURL
  48. }
  49. }
  50. // Populate the entry content.
  51. entry.Content = atomEntry.Content.content()
  52. if entry.Content == "" {
  53. entry.Content = atomEntry.Summary.content()
  54. }
  55. // Populate the entry title.
  56. entry.Title = atomEntry.Title.content()
  57. if entry.Title == "" {
  58. entry.Title = sanitizer.TruncateHTML(entry.Content, 100)
  59. }
  60. if entry.Title == "" {
  61. entry.Title = entry.URL
  62. }
  63. // Populate the entry author.
  64. entry.Author = atomEntry.Author.PersonName()
  65. if entry.Author == "" {
  66. entry.Author = a.atomFeed.Author.PersonName()
  67. }
  68. // Populate the entry date.
  69. for _, value := range []string{atomEntry.Issued, atomEntry.Modified, atomEntry.Created} {
  70. if value = strings.TrimSpace(value); value == "" {
  71. continue
  72. }
  73. parsedDate, err := date.Parse(value)
  74. if err != nil {
  75. slog.Debug("Unable to parse date from Atom 0.3 feed",
  76. slog.String("date", value),
  77. slog.String("id", atomEntry.ID),
  78. slog.Any("error", err),
  79. )
  80. continue
  81. }
  82. entry.Date = parsedDate
  83. break
  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. }