Răsfoiți Sursa

feat(feed): add ignore_entry_updates option to feeds

It introduces a new configuration option `ignore_entry_updates` for feeds, allowing users to skip updating existing entries during scheduled polling.
This is useful when external services (e.g., AI summarizers) modify entry content and users want to preserve those modifications across feed syncs.
Serpicroon 2 luni în urmă
părinte
comite
7b07b8256b
34 a modificat fișierele cu 65 adăugiri și 7 ștergeri
  1. 3 0
      client/model.go
  2. 4 0
      internal/database/migrations.go
  3. 1 0
      internal/locale/translations/de_DE.json
  4. 1 0
      internal/locale/translations/el_EL.json
  5. 1 0
      internal/locale/translations/en_US.json
  6. 1 0
      internal/locale/translations/es_ES.json
  7. 1 0
      internal/locale/translations/fi_FI.json
  8. 1 0
      internal/locale/translations/fr_FR.json
  9. 1 0
      internal/locale/translations/hi_IN.json
  10. 1 0
      internal/locale/translations/id_ID.json
  11. 1 0
      internal/locale/translations/it_IT.json
  12. 1 0
      internal/locale/translations/ja_JP.json
  13. 1 0
      internal/locale/translations/nan_Latn_pehoeji.json
  14. 1 0
      internal/locale/translations/nl_NL.json
  15. 1 0
      internal/locale/translations/pl_PL.json
  16. 1 0
      internal/locale/translations/pt_BR.json
  17. 1 0
      internal/locale/translations/ro_RO.json
  18. 1 0
      internal/locale/translations/ru_RU.json
  19. 1 0
      internal/locale/translations/tr_TR.json
  20. 1 0
      internal/locale/translations/uk_UA.json
  21. 1 0
      internal/locale/translations/zh_CN.json
  22. 1 0
      internal/locale/translations/zh_TW.json
  23. 7 0
      internal/model/feed.go
  24. 6 2
      internal/reader/handler/handler.go
  25. 8 4
      internal/storage/feed.go
  26. 3 1
      internal/storage/feed_query_builder.go
  27. 1 0
      internal/template/templates/views/add_subscription.html
  28. 3 0
      internal/template/templates/views/choose_subscription.html
  29. 1 0
      internal/template/templates/views/edit_feed.html
  30. 1 0
      internal/ui/feed_edit.go
  31. 3 0
      internal/ui/form/feed.go
  32. 2 0
      internal/ui/form/subscription.go
  33. 1 0
      internal/ui/subscription_choose.go
  34. 2 0
      internal/ui/subscription_submit.go

+ 3 - 0
client/model.go

@@ -166,6 +166,7 @@ type Feed struct {
 	BlockFilterEntryRules       string    `json:"block_filter_entry_rules"`
 	BlockFilterEntryRules       string    `json:"block_filter_entry_rules"`
 	KeepFilterEntryRules        string    `json:"keep_filter_entry_rules"`
 	KeepFilterEntryRules        string    `json:"keep_filter_entry_rules"`
 	Crawler                     bool      `json:"crawler"`
 	Crawler                     bool      `json:"crawler"`
+	IgnoreEntryUpdates          bool      `json:"ignore_entry_updates"`
 	UserAgent                   string    `json:"user_agent"`
 	UserAgent                   string    `json:"user_agent"`
 	Cookie                      string    `json:"cookie"`
 	Cookie                      string    `json:"cookie"`
 	Username                    string    `json:"username"`
 	Username                    string    `json:"username"`
@@ -185,6 +186,7 @@ type FeedCreationRequest struct {
 	Username                    string `json:"username"`
 	Username                    string `json:"username"`
 	Password                    string `json:"password"`
 	Password                    string `json:"password"`
 	Crawler                     bool   `json:"crawler"`
 	Crawler                     bool   `json:"crawler"`
+	IgnoreEntryUpdates          bool   `json:"ignore_entry_updates"`
 	Disabled                    bool   `json:"disabled"`
 	Disabled                    bool   `json:"disabled"`
 	IgnoreHTTPCache             bool   `json:"ignore_http_cache"`
 	IgnoreHTTPCache             bool   `json:"ignore_http_cache"`
 	AllowSelfSignedCertificates bool   `json:"allow_self_signed_certificates"`
 	AllowSelfSignedCertificates bool   `json:"allow_self_signed_certificates"`
@@ -214,6 +216,7 @@ type FeedModificationRequest struct {
 	BlockFilterEntryRules       *string `json:"block_filter_entry_rules"`
 	BlockFilterEntryRules       *string `json:"block_filter_entry_rules"`
 	KeepFilterEntryRules        *string `json:"keep_filter_entry_rules"`
 	KeepFilterEntryRules        *string `json:"keep_filter_entry_rules"`
 	Crawler                     *bool   `json:"crawler"`
 	Crawler                     *bool   `json:"crawler"`
+	IgnoreEntryUpdates          *bool   `json:"ignore_entry_updates"`
 	UserAgent                   *string `json:"user_agent"`
 	UserAgent                   *string `json:"user_agent"`
 	Cookie                      *string `json:"cookie"`
 	Cookie                      *string `json:"cookie"`
 	Username                    *string `json:"username"`
 	Username                    *string `json:"username"`

+ 4 - 0
internal/database/migrations.go

@@ -1427,4 +1427,8 @@ var migrations = [...]func(tx *sql.Tx) error{
 		`)
 		`)
 		return err
 		return err
 	},
 	},
+	func(tx *sql.Tx) (err error) {
+		_, err = tx.Exec(`ALTER TABLE feeds ADD COLUMN ignore_entry_updates bool default 'f'`)
+		return err
+	},
 }
 }

+ 1 - 0
internal/locale/translations/de_DE.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Kategorie",
     "form.feed.label.category": "Kategorie",
     "form.feed.label.cookie": "Cookies setzen",
     "form.feed.label.cookie": "Cookies setzen",
     "form.feed.label.crawler": "Originalinhalt herunterladen",
     "form.feed.label.crawler": "Originalinhalt herunterladen",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Beschreibung",
     "form.feed.label.description": "Beschreibung",
     "form.feed.label.disable_http2": "HTTP/2 deaktivieren, um Fingerprinting zu verhindern",
     "form.feed.label.disable_http2": "HTTP/2 deaktivieren, um Fingerprinting zu verhindern",
     "form.feed.label.disabled": "Dieses Abonnement nicht aktualisieren",
     "form.feed.label.disabled": "Dieses Abonnement nicht aktualisieren",

+ 1 - 0
internal/locale/translations/el_EL.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Κατηγορία",
     "form.feed.label.category": "Κατηγορία",
     "form.feed.label.cookie": "Ορισμός Cookies",
     "form.feed.label.cookie": "Ορισμός Cookies",
     "form.feed.label.crawler": "Λήψη αρχικού περιεχομένου",
     "form.feed.label.crawler": "Λήψη αρχικού περιεχομένου",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Περιγραφή",
     "form.feed.label.description": "Περιγραφή",
     "form.feed.label.disable_http2": "Απενεργοποίηση HTTP/2 για αποφυγή δακτυλικών αποτυπωμάτων",
     "form.feed.label.disable_http2": "Απενεργοποίηση HTTP/2 για αποφυγή δακτυλικών αποτυπωμάτων",
     "form.feed.label.disabled": "Μη ανανέωση αυτής της ροής",
     "form.feed.label.disabled": "Μη ανανέωση αυτής της ροής",

+ 1 - 0
internal/locale/translations/en_US.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Category",
     "form.feed.label.category": "Category",
     "form.feed.label.cookie": "Set Cookies",
     "form.feed.label.cookie": "Set Cookies",
     "form.feed.label.crawler": "Fetch original content",
     "form.feed.label.crawler": "Fetch original content",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Description",
     "form.feed.label.description": "Description",
     "form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
     "form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
     "form.feed.label.disabled": "Do not refresh this feed",
     "form.feed.label.disabled": "Do not refresh this feed",

+ 1 - 0
internal/locale/translations/es_ES.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Categoría",
     "form.feed.label.category": "Categoría",
     "form.feed.label.cookie": "Configurar las cookies",
     "form.feed.label.cookie": "Configurar las cookies",
     "form.feed.label.crawler": "Obtener rastreador original",
     "form.feed.label.crawler": "Obtener rastreador original",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Descripción",
     "form.feed.label.description": "Descripción",
     "form.feed.label.disable_http2": "Deshabilite HTTP/2 para evitar huellas digitales",
     "form.feed.label.disable_http2": "Deshabilite HTTP/2 para evitar huellas digitales",
     "form.feed.label.disabled": "No actualice este feed",
     "form.feed.label.disabled": "No actualice este feed",

+ 1 - 0
internal/locale/translations/fi_FI.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Kategoria",
     "form.feed.label.category": "Kategoria",
     "form.feed.label.cookie": "Aseta evästeet",
     "form.feed.label.cookie": "Aseta evästeet",
     "form.feed.label.crawler": "Nouda alkuperäinen sisältö",
     "form.feed.label.crawler": "Nouda alkuperäinen sisältö",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Kuvaus",
     "form.feed.label.description": "Kuvaus",
     "form.feed.label.disable_http2": "Poista HTTP/2 käytöstä sormenjälkien välttämiseksi",
     "form.feed.label.disable_http2": "Poista HTTP/2 käytöstä sormenjälkien välttämiseksi",
     "form.feed.label.disabled": "Älä päivitä tätä syötettä",
     "form.feed.label.disabled": "Älä päivitä tätä syötettä",

+ 1 - 0
internal/locale/translations/fr_FR.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Catégorie",
     "form.feed.label.category": "Catégorie",
     "form.feed.label.cookie": "Définir les cookies",
     "form.feed.label.cookie": "Définir les cookies",
     "form.feed.label.crawler": "Récupérer le contenu original",
     "form.feed.label.crawler": "Récupérer le contenu original",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Description",
     "form.feed.label.description": "Description",
     "form.feed.label.disable_http2": "Désactiver HTTP/2",
     "form.feed.label.disable_http2": "Désactiver HTTP/2",
     "form.feed.label.disabled": "Ne pas actualiser ce flux",
     "form.feed.label.disabled": "Ne pas actualiser ce flux",

+ 1 - 0
internal/locale/translations/hi_IN.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "श्रेणी",
     "form.feed.label.category": "श्रेणी",
     "form.feed.label.cookie": "कुकीज़ सेट करें",
     "form.feed.label.cookie": "कुकीज़ सेट करें",
     "form.feed.label.crawler": "मूल सामग्री प्राप्त करें",
     "form.feed.label.crawler": "मूल सामग्री प्राप्त करें",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "विवरण",
     "form.feed.label.description": "विवरण",
     "form.feed.label.disable_http2": "फिंगरप्रिंटिंग से बचने के लिए HTTP/2 अक्षम करें",
     "form.feed.label.disable_http2": "फिंगरप्रिंटिंग से बचने के लिए HTTP/2 अक्षम करें",
     "form.feed.label.disabled": "इस फ़ीड को रीफ़्रेश न करें",
     "form.feed.label.disabled": "इस फ़ीड को रीफ़्रेश न करें",

+ 1 - 0
internal/locale/translations/id_ID.json

@@ -172,6 +172,7 @@
     "form.feed.label.category": "Kategori",
     "form.feed.label.category": "Kategori",
     "form.feed.label.cookie": "Atur Kuki",
     "form.feed.label.cookie": "Atur Kuki",
     "form.feed.label.crawler": "Ambil konten asli",
     "form.feed.label.crawler": "Ambil konten asli",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Deskripsi",
     "form.feed.label.description": "Deskripsi",
     "form.feed.label.disable_http2": "Matikan HTTP/2 untuk menghindari pelacakan",
     "form.feed.label.disable_http2": "Matikan HTTP/2 untuk menghindari pelacakan",
     "form.feed.label.disabled": "Jangan perbarui umpan ini",
     "form.feed.label.disabled": "Jangan perbarui umpan ini",

+ 1 - 0
internal/locale/translations/it_IT.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Categoria",
     "form.feed.label.category": "Categoria",
     "form.feed.label.cookie": "Installare i cookies",
     "form.feed.label.cookie": "Installare i cookies",
     "form.feed.label.crawler": "Scarica il contenuto integrale",
     "form.feed.label.crawler": "Scarica il contenuto integrale",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Descrizione",
     "form.feed.label.description": "Descrizione",
     "form.feed.label.disable_http2": "Disabilita HTTP/2 per evitare il fingerprinting",
     "form.feed.label.disable_http2": "Disabilita HTTP/2 per evitare il fingerprinting",
     "form.feed.label.disabled": "Non aggiornare questo feed",
     "form.feed.label.disabled": "Non aggiornare questo feed",

+ 1 - 0
internal/locale/translations/ja_JP.json

@@ -172,6 +172,7 @@
     "form.feed.label.category": "カテゴリ",
     "form.feed.label.category": "カテゴリ",
     "form.feed.label.cookie": "Cookie の設定",
     "form.feed.label.cookie": "Cookie の設定",
     "form.feed.label.crawler": "オリジナルの内容を取得",
     "form.feed.label.crawler": "オリジナルの内容を取得",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "説明",
     "form.feed.label.description": "説明",
     "form.feed.label.disable_http2": "フィンガープリンティング回避のため HTTP/2 を無効化",
     "form.feed.label.disable_http2": "フィンガープリンティング回避のため HTTP/2 を無効化",
     "form.feed.label.disabled": "このフィードを更新しない",
     "form.feed.label.disabled": "このフィードを更新しない",

+ 1 - 0
internal/locale/translations/nan_Latn_pehoeji.json

@@ -172,6 +172,7 @@
     "form.feed.label.category": "lūi-pia̍t",
     "form.feed.label.category": "lūi-pia̍t",
     "form.feed.label.cookie": "Siat-tēng Cookies",
     "form.feed.label.cookie": "Siat-tēng Cookies",
     "form.feed.label.crawler": "Lia̍h goân-tóe lōe-iông",
     "form.feed.label.crawler": "Lia̍h goân-tóe lōe-iông",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Biâu-su̍t",
     "form.feed.label.description": "Biâu-su̍t",
     "form.feed.label.disable_http2": "Thêng iōng HTTP/2 pī-bián chéng-thâu-á-hûn tui-chong",
     "form.feed.label.disable_http2": "Thêng iōng HTTP/2 pī-bián chéng-thâu-á-hûn tui-chong",
     "form.feed.label.disabled": "Mài tha̍k chit ê siau-sit lâi-goân ê sin siau-sit",
     "form.feed.label.disabled": "Mài tha̍k chit ê siau-sit lâi-goân ê sin siau-sit",

+ 1 - 0
internal/locale/translations/nl_NL.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Categorie",
     "form.feed.label.category": "Categorie",
     "form.feed.label.cookie": "Cookies instellen",
     "form.feed.label.cookie": "Cookies instellen",
     "form.feed.label.crawler": "Download originele inhoud",
     "form.feed.label.crawler": "Download originele inhoud",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Omschrijving",
     "form.feed.label.description": "Omschrijving",
     "form.feed.label.disable_http2": "HTTP/2 uitschakelen om fingerprinting te voorkomen",
     "form.feed.label.disable_http2": "HTTP/2 uitschakelen om fingerprinting te voorkomen",
     "form.feed.label.disabled": "Deze feed niet vernieuwen",
     "form.feed.label.disabled": "Deze feed niet vernieuwen",

+ 1 - 0
internal/locale/translations/pl_PL.json

@@ -178,6 +178,7 @@
     "form.feed.label.category": "Kategoria",
     "form.feed.label.category": "Kategoria",
     "form.feed.label.cookie": "Ustaw ciasteczka",
     "form.feed.label.cookie": "Ustaw ciasteczka",
     "form.feed.label.crawler": "Pobierz oryginalną treść",
     "form.feed.label.crawler": "Pobierz oryginalną treść",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Opis",
     "form.feed.label.description": "Opis",
     "form.feed.label.disable_http2": "Wyłącz protokół HTTP/2, aby uniknąć identyfikowania",
     "form.feed.label.disable_http2": "Wyłącz protokół HTTP/2, aby uniknąć identyfikowania",
     "form.feed.label.disabled": "Nie aktualizuj tego kanału",
     "form.feed.label.disabled": "Nie aktualizuj tego kanału",

+ 1 - 0
internal/locale/translations/pt_BR.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Categoria",
     "form.feed.label.category": "Categoria",
     "form.feed.label.cookie": "Definir Cookies",
     "form.feed.label.cookie": "Definir Cookies",
     "form.feed.label.crawler": "Obter conteúdo original",
     "form.feed.label.crawler": "Obter conteúdo original",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Descrição",
     "form.feed.label.description": "Descrição",
     "form.feed.label.disable_http2": "Desativar HTTP/2 para evitar fingerprinting",
     "form.feed.label.disable_http2": "Desativar HTTP/2 para evitar fingerprinting",
     "form.feed.label.disabled": "Não atualizar esta fonte",
     "form.feed.label.disabled": "Não atualizar esta fonte",

+ 1 - 0
internal/locale/translations/ro_RO.json

@@ -178,6 +178,7 @@
     "form.feed.label.category": "Categorie",
     "form.feed.label.category": "Categorie",
     "form.feed.label.cookie": "Setare Cookie-uri",
     "form.feed.label.cookie": "Setare Cookie-uri",
     "form.feed.label.crawler": "Aduce conținutul original",
     "form.feed.label.crawler": "Aduce conținutul original",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Descriere",
     "form.feed.label.description": "Descriere",
     "form.feed.label.disable_http2": "Dezactivează HTTP/2 pentru a preveni amprentarea",
     "form.feed.label.disable_http2": "Dezactivează HTTP/2 pentru a preveni amprentarea",
     "form.feed.label.disabled": "Nu actualiza acest flux",
     "form.feed.label.disabled": "Nu actualiza acest flux",

+ 1 - 0
internal/locale/translations/ru_RU.json

@@ -178,6 +178,7 @@
     "form.feed.label.category": "Категория",
     "form.feed.label.category": "Категория",
     "form.feed.label.cookie": "Установить куки",
     "form.feed.label.cookie": "Установить куки",
     "form.feed.label.crawler": "Извлечь оригинальное содержимое",
     "form.feed.label.crawler": "Извлечь оригинальное содержимое",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Описание",
     "form.feed.label.description": "Описание",
     "form.feed.label.disable_http2": "Отключить HTTP/2 для предотвращения фингерпринтинга",
     "form.feed.label.disable_http2": "Отключить HTTP/2 для предотвращения фингерпринтинга",
     "form.feed.label.disabled": "Не обновлять эту подписку",
     "form.feed.label.disabled": "Не обновлять эту подписку",

+ 1 - 0
internal/locale/translations/tr_TR.json

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Kategori",
     "form.feed.label.category": "Kategori",
     "form.feed.label.cookie": "Çerezleri Ayarla",
     "form.feed.label.cookie": "Çerezleri Ayarla",
     "form.feed.label.crawler": "Orijinal içeriği çek",
     "form.feed.label.crawler": "Orijinal içeriği çek",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Açıklama",
     "form.feed.label.description": "Açıklama",
     "form.feed.label.disable_http2": "Parmak izini önlemek için HTTP/2'yi devre dışı bırakın",
     "form.feed.label.disable_http2": "Parmak izini önlemek için HTTP/2'yi devre dışı bırakın",
     "form.feed.label.disabled": "Bu beslemeyi yenileme",
     "form.feed.label.disabled": "Bu beslemeyi yenileme",

+ 1 - 0
internal/locale/translations/uk_UA.json

@@ -178,6 +178,7 @@
     "form.feed.label.category": "Категорія",
     "form.feed.label.category": "Категорія",
     "form.feed.label.cookie": "Встановити кукі",
     "form.feed.label.cookie": "Встановити кукі",
     "form.feed.label.crawler": "Завантажувати оригінальний вміст",
     "form.feed.label.crawler": "Завантажувати оригінальний вміст",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Опис",
     "form.feed.label.description": "Опис",
     "form.feed.label.disable_http2": "Вимкнути HTTP/2 для уникнення відбитків",
     "form.feed.label.disable_http2": "Вимкнути HTTP/2 для уникнення відбитків",
     "form.feed.label.disabled": "Не оновлювати цю стрічку",
     "form.feed.label.disabled": "Не оновлювати цю стрічку",

+ 1 - 0
internal/locale/translations/zh_CN.json

@@ -172,6 +172,7 @@
     "form.feed.label.category": "分类",
     "form.feed.label.category": "分类",
     "form.feed.label.cookie": "设置 Cookie",
     "form.feed.label.cookie": "设置 Cookie",
     "form.feed.label.crawler": "获取原始内容",
     "form.feed.label.crawler": "获取原始内容",
+    "form.feed.label.ignore_entry_updates": "忽略条目更新",
     "form.feed.label.description": "描述",
     "form.feed.label.description": "描述",
     "form.feed.label.disable_http2": "禁用 HTTP/2 以避免指纹识别",
     "form.feed.label.disable_http2": "禁用 HTTP/2 以避免指纹识别",
     "form.feed.label.disabled": "不刷新此订阅",
     "form.feed.label.disabled": "不刷新此订阅",

+ 1 - 0
internal/locale/translations/zh_TW.json

@@ -172,6 +172,7 @@
     "form.feed.label.category": "類別",
     "form.feed.label.category": "類別",
     "form.feed.label.cookie": "設定 Cookies",
     "form.feed.label.cookie": "設定 Cookies",
     "form.feed.label.crawler": "下載原文內容",
     "form.feed.label.crawler": "下載原文內容",
+    "form.feed.label.ignore_entry_updates": "忽略條目更新",
     "form.feed.label.description": "描述",
     "form.feed.label.description": "描述",
     "form.feed.label.disable_http2": "停用 HTTP/2 以避免指紋追蹤",
     "form.feed.label.disable_http2": "停用 HTTP/2 以避免指紋追蹤",
     "form.feed.label.disabled": "不要更新此 Feed",
     "form.feed.label.disabled": "不要更新此 Feed",

+ 7 - 0
internal/model/feed.go

@@ -55,6 +55,7 @@ type Feed struct {
 	PushoverEnabled             bool      `json:"pushover_enabled"`
 	PushoverEnabled             bool      `json:"pushover_enabled"`
 	NtfyEnabled                 bool      `json:"ntfy_enabled"`
 	NtfyEnabled                 bool      `json:"ntfy_enabled"`
 	Crawler                     bool      `json:"crawler"`
 	Crawler                     bool      `json:"crawler"`
+	IgnoreEntryUpdates          bool      `json:"ignore_entry_updates"`
 	AppriseServiceURLs          string    `json:"apprise_service_urls"`
 	AppriseServiceURLs          string    `json:"apprise_service_urls"`
 	WebhookURL                  string    `json:"webhook_url"`
 	WebhookURL                  string    `json:"webhook_url"`
 	NtfyPriority                int       `json:"ntfy_priority"`
 	NtfyPriority                int       `json:"ntfy_priority"`
@@ -156,6 +157,7 @@ type FeedCreationRequest struct {
 	Username                    string `json:"username"`
 	Username                    string `json:"username"`
 	Password                    string `json:"password"`
 	Password                    string `json:"password"`
 	Crawler                     bool   `json:"crawler"`
 	Crawler                     bool   `json:"crawler"`
+	IgnoreEntryUpdates          bool   `json:"ignore_entry_updates"`
 	Disabled                    bool   `json:"disabled"`
 	Disabled                    bool   `json:"disabled"`
 	NoMediaPlayer               bool   `json:"no_media_player"`
 	NoMediaPlayer               bool   `json:"no_media_player"`
 	IgnoreHTTPCache             bool   `json:"ignore_http_cache"`
 	IgnoreHTTPCache             bool   `json:"ignore_http_cache"`
@@ -195,6 +197,7 @@ type FeedModificationRequest struct {
 	BlockFilterEntryRules       *string `json:"block_filter_entry_rules"`
 	BlockFilterEntryRules       *string `json:"block_filter_entry_rules"`
 	KeepFilterEntryRules        *string `json:"keep_filter_entry_rules"`
 	KeepFilterEntryRules        *string `json:"keep_filter_entry_rules"`
 	Crawler                     *bool   `json:"crawler"`
 	Crawler                     *bool   `json:"crawler"`
+	IgnoreEntryUpdates          *bool   `json:"ignore_entry_updates"`
 	UserAgent                   *string `json:"user_agent"`
 	UserAgent                   *string `json:"user_agent"`
 	Cookie                      *string `json:"cookie"`
 	Cookie                      *string `json:"cookie"`
 	Username                    *string `json:"username"`
 	Username                    *string `json:"username"`
@@ -260,6 +263,10 @@ func (f *FeedModificationRequest) Patch(feed *Feed) {
 		feed.Crawler = *f.Crawler
 		feed.Crawler = *f.Crawler
 	}
 	}
 
 
+	if f.IgnoreEntryUpdates != nil {
+		feed.IgnoreEntryUpdates = *f.IgnoreEntryUpdates
+	}
+
 	if f.UserAgent != nil {
 	if f.UserAgent != nil {
 		feed.UserAgent = *f.UserAgent
 		feed.UserAgent = *f.UserAgent
 	}
 	}

+ 6 - 2
internal/reader/handler/handler.go

@@ -53,6 +53,7 @@ func CreateFeedFromSubscriptionDiscovery(store *storage.Storage, userID int64, f
 	subscription.Username = feedCreationRequest.Username
 	subscription.Username = feedCreationRequest.Username
 	subscription.Password = feedCreationRequest.Password
 	subscription.Password = feedCreationRequest.Password
 	subscription.Crawler = feedCreationRequest.Crawler
 	subscription.Crawler = feedCreationRequest.Crawler
+	subscription.IgnoreEntryUpdates = feedCreationRequest.IgnoreEntryUpdates
 	subscription.Disabled = feedCreationRequest.Disabled
 	subscription.Disabled = feedCreationRequest.Disabled
 	subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache
 	subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache
 	subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates
 	subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates
@@ -154,6 +155,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
 	subscription.Username = feedCreationRequest.Username
 	subscription.Username = feedCreationRequest.Username
 	subscription.Password = feedCreationRequest.Password
 	subscription.Password = feedCreationRequest.Password
 	subscription.Crawler = feedCreationRequest.Crawler
 	subscription.Crawler = feedCreationRequest.Crawler
+	subscription.IgnoreEntryUpdates = feedCreationRequest.IgnoreEntryUpdates
 	subscription.Disabled = feedCreationRequest.Disabled
 	subscription.Disabled = feedCreationRequest.Disabled
 	subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache
 	subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache
 	subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates
 	subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates
@@ -338,8 +340,10 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64, forceRefresh bool
 		originalFeed.Entries = updatedFeed.Entries
 		originalFeed.Entries = updatedFeed.Entries
 		processor.ProcessFeedEntries(store, originalFeed, userID, forceRefresh)
 		processor.ProcessFeedEntries(store, originalFeed, userID, forceRefresh)
 
 
-		// We don't update existing entries when the crawler is enabled (we crawl only inexisting entries). Unless it is forced to refresh
-		updateExistingEntries := forceRefresh || !originalFeed.Crawler
+		// We don't update existing entries when the crawler is enabled (we crawl only inexisting entries).
+		// We also skip updating existing entries if the feed has ignore_entry_updates enabled.
+		// Unless it is forced to refresh.
+		updateExistingEntries := forceRefresh || (!originalFeed.Crawler && !originalFeed.IgnoreEntryUpdates)
 		newEntries, storeErr := store.RefreshFeedEntries(originalFeed.UserID, originalFeed.ID, originalFeed.Entries, updateExistingEntries)
 		newEntries, storeErr := store.RefreshFeedEntries(originalFeed.UserID, originalFeed.ID, originalFeed.Entries, updateExistingEntries)
 		if storeErr != nil {
 		if storeErr != nil {
 			localizedError := locale.NewLocalizedErrorWrapper(storeErr, "error.database_error", storeErr)
 			localizedError := locale.NewLocalizedErrorWrapper(storeErr, "error.database_error", storeErr)

+ 8 - 4
internal/storage/feed.go

@@ -263,10 +263,11 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
 			webhook_url,
 			webhook_url,
 			disable_http2,
 			disable_http2,
 			description,
 			description,
-			proxy_url
+			proxy_url,
+			ignore_entry_updates
 		)
 		)
 		VALUES
 		VALUES
-			($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30)
+			($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31)
 		RETURNING
 		RETURNING
 			id
 			id
 	`
 	`
@@ -302,6 +303,7 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
 		feed.DisableHTTP2,
 		feed.DisableHTTP2,
 		feed.Description,
 		feed.Description,
 		feed.ProxyURL,
 		feed.ProxyURL,
+		feed.IgnoreEntryUpdates,
 	).Scan(&feed.ID)
 	).Scan(&feed.ID)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf(`store: unable to create feed %q: %v`, feed.FeedURL, err)
 		return fmt.Errorf(`store: unable to create feed %q: %v`, feed.FeedURL, err)
@@ -384,9 +386,10 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
 			ntfy_topic=$35,
 			ntfy_topic=$35,
 			pushover_enabled=$36,
 			pushover_enabled=$36,
 			pushover_priority=$37,
 			pushover_priority=$37,
-			proxy_url=$38
+			proxy_url=$38,
+			ignore_entry_updates=$39
 		WHERE
 		WHERE
-			id=$39 AND user_id=$40
+			id=$40 AND user_id=$41
 	`
 	`
 	_, err = s.db.Exec(query,
 	_, err = s.db.Exec(query,
 		feed.FeedURL,
 		feed.FeedURL,
@@ -427,6 +430,7 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
 		feed.PushoverEnabled,
 		feed.PushoverEnabled,
 		feed.PushoverPriority,
 		feed.PushoverPriority,
 		feed.ProxyURL,
 		feed.ProxyURL,
+		feed.IgnoreEntryUpdates,
 		feed.ID,
 		feed.ID,
 		feed.UserID,
 		feed.UserID,
 	)
 	)

+ 3 - 1
internal/storage/feed_query_builder.go

@@ -176,7 +176,8 @@ func (f *feedQueryBuilder) GetFeeds() (model.Feeds, error) {
 			f.ntfy_topic,
 			f.ntfy_topic,
 			f.pushover_enabled,
 			f.pushover_enabled,
 			f.pushover_priority,
 			f.pushover_priority,
-			f.proxy_url
+			f.proxy_url,
+			f.ignore_entry_updates
 		FROM
 		FROM
 			feeds f
 			feeds f
 		LEFT JOIN
 		LEFT JOIN
@@ -258,6 +259,7 @@ func (f *feedQueryBuilder) GetFeeds() (model.Feeds, error) {
 			&feed.PushoverEnabled,
 			&feed.PushoverEnabled,
 			&feed.PushoverPriority,
 			&feed.PushoverPriority,
 			&feed.ProxyURL,
 			&feed.ProxyURL,
+			&feed.IgnoreEntryUpdates,
 		)
 		)
 
 
 		if err != nil {
 		if err != nil {

+ 1 - 0
internal/template/templates/views/add_subscription.html

@@ -32,6 +32,7 @@
             <summary>{{ t "page.add_feed.legend.advanced_options" }}</summary>
             <summary>{{ t "page.add_feed.legend.advanced_options" }}</summary>
             <div class="details-content">
             <div class="details-content">
                 <label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
                 <label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
+                <label><input type="checkbox" name="ignore_entry_updates" value="1" {{ if .form.IgnoreEntryUpdates }}checked{{ end }}> {{ t "form.feed.label.ignore_entry_updates" }}</label>
                 <label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label>
                 <label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label>
                 <label><input type="checkbox" name="disable_http2" value="1" {{ if .form.DisableHTTP2 }}checked{{ end }}> {{ t "form.feed.label.disable_http2" }}</label>
                 <label><input type="checkbox" name="disable_http2" value="1" {{ if .form.DisableHTTP2 }}checked{{ end }}> {{ t "form.feed.label.disable_http2" }}</label>
 
 

+ 3 - 0
internal/template/templates/views/choose_subscription.html

@@ -29,6 +29,9 @@
     {{ if .form.Crawler }}
     {{ if .form.Crawler }}
         <input type="hidden" name="crawler" value="1">
         <input type="hidden" name="crawler" value="1">
     {{ end }}
     {{ end }}
+    {{ if .form.IgnoreEntryUpdates }}
+        <input type="hidden" name="ignore_entry_updates" value="1">
+    {{ end }}
     {{ if .form.AllowSelfSignedCertificates }}
     {{ if .form.AllowSelfSignedCertificates }}
         <input type="hidden" name="allow_self_signed_certificates" value="1">
         <input type="hidden" name="allow_self_signed_certificates" value="1">
     {{ end }}
     {{ end }}

+ 1 - 0
internal/template/templates/views/edit_feed.html

@@ -104,6 +104,7 @@
             <input type="text" name="cookie" id="form-cookie" value="{{ .form.Cookie }}" spellcheck="false">
             <input type="text" name="cookie" id="form-cookie" value="{{ .form.Cookie }}" spellcheck="false">
 
 
             <label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
             <label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
+            <label><input type="checkbox" name="ignore_entry_updates" value="1" {{ if .form.IgnoreEntryUpdates }}checked{{ end }}> {{ t "form.feed.label.ignore_entry_updates" }}</label>
             <label><input type="checkbox" name="ignore_http_cache" value="1" {{ if .form.IgnoreHTTPCache }}checked{{ end }}> {{ t "form.feed.label.ignore_http_cache" }}</label>
             <label><input type="checkbox" name="ignore_http_cache" value="1" {{ if .form.IgnoreHTTPCache }}checked{{ end }}> {{ t "form.feed.label.ignore_http_cache" }}</label>
             <label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label>
             <label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label>
             <label><input type="checkbox" name="disable_http2" value="1" {{ if .form.DisableHTTP2 }}checked{{ end }}> {{ t "form.feed.label.disable_http2" }}</label>
             <label><input type="checkbox" name="disable_http2" value="1" {{ if .form.DisableHTTP2 }}checked{{ end }}> {{ t "form.feed.label.disable_http2" }}</label>

+ 1 - 0
internal/ui/feed_edit.go

@@ -52,6 +52,7 @@ func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) {
 		BlockFilterEntryRules:       feed.BlockFilterEntryRules,
 		BlockFilterEntryRules:       feed.BlockFilterEntryRules,
 		KeepFilterEntryRules:        feed.KeepFilterEntryRules,
 		KeepFilterEntryRules:        feed.KeepFilterEntryRules,
 		Crawler:                     feed.Crawler,
 		Crawler:                     feed.Crawler,
+		IgnoreEntryUpdates:          feed.IgnoreEntryUpdates,
 		UserAgent:                   feed.UserAgent,
 		UserAgent:                   feed.UserAgent,
 		Cookie:                      feed.Cookie,
 		Cookie:                      feed.Cookie,
 		CategoryID:                  feed.Category.ID,
 		CategoryID:                  feed.Category.ID,

+ 3 - 0
internal/ui/form/feed.go

@@ -24,6 +24,7 @@ type FeedForm struct {
 	BlockFilterEntryRules       string
 	BlockFilterEntryRules       string
 	KeepFilterEntryRules        string
 	KeepFilterEntryRules        string
 	Crawler                     bool
 	Crawler                     bool
+	IgnoreEntryUpdates          bool
 	UserAgent                   string
 	UserAgent                   string
 	Cookie                      string
 	Cookie                      string
 	CategoryID                  int64
 	CategoryID                  int64
@@ -62,6 +63,7 @@ func (f FeedForm) Merge(feed *model.Feed) *model.Feed {
 	feed.BlockFilterEntryRules = f.BlockFilterEntryRules
 	feed.BlockFilterEntryRules = f.BlockFilterEntryRules
 	feed.KeepFilterEntryRules = f.KeepFilterEntryRules
 	feed.KeepFilterEntryRules = f.KeepFilterEntryRules
 	feed.Crawler = f.Crawler
 	feed.Crawler = f.Crawler
+	feed.IgnoreEntryUpdates = f.IgnoreEntryUpdates
 	feed.UserAgent = f.UserAgent
 	feed.UserAgent = f.UserAgent
 	feed.Cookie = f.Cookie
 	feed.Cookie = f.Cookie
 	feed.ParsingErrorCount = 0
 	feed.ParsingErrorCount = 0
@@ -118,6 +120,7 @@ func NewFeedForm(r *http.Request) *FeedForm {
 		BlockFilterEntryRules:       r.FormValue("block_filter_entry_rules"),
 		BlockFilterEntryRules:       r.FormValue("block_filter_entry_rules"),
 		KeepFilterEntryRules:        r.FormValue("keep_filter_entry_rules"),
 		KeepFilterEntryRules:        r.FormValue("keep_filter_entry_rules"),
 		Crawler:                     r.FormValue("crawler") == "1",
 		Crawler:                     r.FormValue("crawler") == "1",
+		IgnoreEntryUpdates:          r.FormValue("ignore_entry_updates") == "1",
 		CategoryID:                  int64(categoryID),
 		CategoryID:                  int64(categoryID),
 		Username:                    r.FormValue("feed_username"),
 		Username:                    r.FormValue("feed_username"),
 		Password:                    r.FormValue("feed_password"),
 		Password:                    r.FormValue("feed_password"),

+ 2 - 0
internal/ui/form/subscription.go

@@ -16,6 +16,7 @@ type SubscriptionForm struct {
 	URL                         string
 	URL                         string
 	CategoryID                  int64
 	CategoryID                  int64
 	Crawler                     bool
 	Crawler                     bool
+	IgnoreEntryUpdates          bool
 	FetchViaProxy               bool
 	FetchViaProxy               bool
 	AllowSelfSignedCertificates bool
 	AllowSelfSignedCertificates bool
 	UserAgent                   string
 	UserAgent                   string
@@ -73,6 +74,7 @@ func NewSubscriptionForm(r *http.Request) *SubscriptionForm {
 		URL:                         r.FormValue("url"),
 		URL:                         r.FormValue("url"),
 		CategoryID:                  int64(categoryID),
 		CategoryID:                  int64(categoryID),
 		Crawler:                     r.FormValue("crawler") == "1",
 		Crawler:                     r.FormValue("crawler") == "1",
+		IgnoreEntryUpdates:          r.FormValue("ignore_entry_updates") == "1",
 		AllowSelfSignedCertificates: r.FormValue("allow_self_signed_certificates") == "1",
 		AllowSelfSignedCertificates: r.FormValue("allow_self_signed_certificates") == "1",
 		FetchViaProxy:               r.FormValue("fetch_via_proxy") == "1",
 		FetchViaProxy:               r.FormValue("fetch_via_proxy") == "1",
 		UserAgent:                   r.FormValue("user_agent"),
 		UserAgent:                   r.FormValue("user_agent"),

+ 1 - 0
internal/ui/subscription_choose.go

@@ -51,6 +51,7 @@ func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Requ
 		CategoryID:                  subscriptionForm.CategoryID,
 		CategoryID:                  subscriptionForm.CategoryID,
 		FeedURL:                     subscriptionForm.URL,
 		FeedURL:                     subscriptionForm.URL,
 		Crawler:                     subscriptionForm.Crawler,
 		Crawler:                     subscriptionForm.Crawler,
+		IgnoreEntryUpdates:          subscriptionForm.IgnoreEntryUpdates,
 		AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 		AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 		UserAgent:                   subscriptionForm.UserAgent,
 		UserAgent:                   subscriptionForm.UserAgent,
 		Cookie:                      subscriptionForm.Cookie,
 		Cookie:                      subscriptionForm.Cookie,

+ 2 - 0
internal/ui/subscription_submit.go

@@ -100,6 +100,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 				FeedURL:                     subscriptions[0].URL,
 				FeedURL:                     subscriptions[0].URL,
 				AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 				AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 				Crawler:                     subscriptionForm.Crawler,
 				Crawler:                     subscriptionForm.Crawler,
+				IgnoreEntryUpdates:          subscriptionForm.IgnoreEntryUpdates,
 				UserAgent:                   subscriptionForm.UserAgent,
 				UserAgent:                   subscriptionForm.UserAgent,
 				Cookie:                      subscriptionForm.Cookie,
 				Cookie:                      subscriptionForm.Cookie,
 				Username:                    subscriptionForm.Username,
 				Username:                    subscriptionForm.Username,
@@ -129,6 +130,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 			CategoryID:                  subscriptionForm.CategoryID,
 			CategoryID:                  subscriptionForm.CategoryID,
 			FeedURL:                     subscriptions[0].URL,
 			FeedURL:                     subscriptions[0].URL,
 			Crawler:                     subscriptionForm.Crawler,
 			Crawler:                     subscriptionForm.Crawler,
+			IgnoreEntryUpdates:          subscriptionForm.IgnoreEntryUpdates,
 			AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 			AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 			UserAgent:                   subscriptionForm.UserAgent,
 			UserAgent:                   subscriptionForm.UserAgent,
 			Cookie:                      subscriptionForm.Cookie,
 			Cookie:                      subscriptionForm.Cookie,