Procházet zdrojové kódy

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 před 1 měsícem
rodič
revize
7b07b8256b
34 změnil soubory, kde provedl 65 přidání a 7 odebrání
  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"`
 	KeepFilterEntryRules        string    `json:"keep_filter_entry_rules"`
 	Crawler                     bool      `json:"crawler"`
+	IgnoreEntryUpdates          bool      `json:"ignore_entry_updates"`
 	UserAgent                   string    `json:"user_agent"`
 	Cookie                      string    `json:"cookie"`
 	Username                    string    `json:"username"`
@@ -185,6 +186,7 @@ type FeedCreationRequest struct {
 	Username                    string `json:"username"`
 	Password                    string `json:"password"`
 	Crawler                     bool   `json:"crawler"`
+	IgnoreEntryUpdates          bool   `json:"ignore_entry_updates"`
 	Disabled                    bool   `json:"disabled"`
 	IgnoreHTTPCache             bool   `json:"ignore_http_cache"`
 	AllowSelfSignedCertificates bool   `json:"allow_self_signed_certificates"`
@@ -214,6 +216,7 @@ type FeedModificationRequest struct {
 	BlockFilterEntryRules       *string `json:"block_filter_entry_rules"`
 	KeepFilterEntryRules        *string `json:"keep_filter_entry_rules"`
 	Crawler                     *bool   `json:"crawler"`
+	IgnoreEntryUpdates          *bool   `json:"ignore_entry_updates"`
 	UserAgent                   *string `json:"user_agent"`
 	Cookie                      *string `json:"cookie"`
 	Username                    *string `json:"username"`

+ 4 - 0
internal/database/migrations.go

@@ -1427,4 +1427,8 @@ var migrations = [...]func(tx *sql.Tx) error{
 		`)
 		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.cookie": "Cookies setzen",
     "form.feed.label.crawler": "Originalinhalt herunterladen",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Beschreibung",
     "form.feed.label.disable_http2": "HTTP/2 deaktivieren, um Fingerprinting zu verhindern",
     "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.cookie": "Ορισμός Cookies",
     "form.feed.label.crawler": "Λήψη αρχικού περιεχομένου",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Περιγραφή",
     "form.feed.label.disable_http2": "Απενεργοποίηση HTTP/2 για αποφυγή δακτυλικών αποτυπωμάτων",
     "form.feed.label.disabled": "Μη ανανέωση αυτής της ροής",

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

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Category",
     "form.feed.label.cookie": "Set Cookies",
     "form.feed.label.crawler": "Fetch original content",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Description",
     "form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
     "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.cookie": "Configurar las cookies",
     "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.disable_http2": "Deshabilite HTTP/2 para evitar huellas digitales",
     "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.cookie": "Aseta evästeet",
     "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.disable_http2": "Poista HTTP/2 käytöstä sormenjälkien välttämiseksi",
     "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.cookie": "Définir les cookies",
     "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.disable_http2": "Désactiver HTTP/2",
     "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.cookie": "कुकीज़ सेट करें",
     "form.feed.label.crawler": "मूल सामग्री प्राप्त करें",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "विवरण",
     "form.feed.label.disable_http2": "फिंगरप्रिंटिंग से बचने के लिए HTTP/2 अक्षम करें",
     "form.feed.label.disabled": "इस फ़ीड को रीफ़्रेश न करें",

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

@@ -172,6 +172,7 @@
     "form.feed.label.category": "Kategori",
     "form.feed.label.cookie": "Atur Kuki",
     "form.feed.label.crawler": "Ambil konten asli",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Deskripsi",
     "form.feed.label.disable_http2": "Matikan HTTP/2 untuk menghindari pelacakan",
     "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.cookie": "Installare i cookies",
     "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.disable_http2": "Disabilita HTTP/2 per evitare il fingerprinting",
     "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.cookie": "Cookie の設定",
     "form.feed.label.crawler": "オリジナルの内容を取得",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "説明",
     "form.feed.label.disable_http2": "フィンガープリンティング回避のため HTTP/2 を無効化",
     "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.cookie": "Siat-tēng Cookies",
     "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.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",

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

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Categorie",
     "form.feed.label.cookie": "Cookies instellen",
     "form.feed.label.crawler": "Download originele inhoud",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Omschrijving",
     "form.feed.label.disable_http2": "HTTP/2 uitschakelen om fingerprinting te voorkomen",
     "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.cookie": "Ustaw ciasteczka",
     "form.feed.label.crawler": "Pobierz oryginalną treść",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Opis",
     "form.feed.label.disable_http2": "Wyłącz protokół HTTP/2, aby uniknąć identyfikowania",
     "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.cookie": "Definir Cookies",
     "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.disable_http2": "Desativar HTTP/2 para evitar fingerprinting",
     "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.cookie": "Setare Cookie-uri",
     "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.disable_http2": "Dezactivează HTTP/2 pentru a preveni amprentarea",
     "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.cookie": "Установить куки",
     "form.feed.label.crawler": "Извлечь оригинальное содержимое",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Описание",
     "form.feed.label.disable_http2": "Отключить HTTP/2 для предотвращения фингерпринтинга",
     "form.feed.label.disabled": "Не обновлять эту подписку",

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

@@ -175,6 +175,7 @@
     "form.feed.label.category": "Kategori",
     "form.feed.label.cookie": "Çerezleri Ayarla",
     "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.disable_http2": "Parmak izini önlemek için HTTP/2'yi devre dışı bırakın",
     "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.cookie": "Встановити кукі",
     "form.feed.label.crawler": "Завантажувати оригінальний вміст",
+    "form.feed.label.ignore_entry_updates": "Ignore entry updates",
     "form.feed.label.description": "Опис",
     "form.feed.label.disable_http2": "Вимкнути HTTP/2 для уникнення відбитків",
     "form.feed.label.disabled": "Не оновлювати цю стрічку",

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

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

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

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

+ 7 - 0
internal/model/feed.go

@@ -55,6 +55,7 @@ type Feed struct {
 	PushoverEnabled             bool      `json:"pushover_enabled"`
 	NtfyEnabled                 bool      `json:"ntfy_enabled"`
 	Crawler                     bool      `json:"crawler"`
+	IgnoreEntryUpdates          bool      `json:"ignore_entry_updates"`
 	AppriseServiceURLs          string    `json:"apprise_service_urls"`
 	WebhookURL                  string    `json:"webhook_url"`
 	NtfyPriority                int       `json:"ntfy_priority"`
@@ -156,6 +157,7 @@ type FeedCreationRequest struct {
 	Username                    string `json:"username"`
 	Password                    string `json:"password"`
 	Crawler                     bool   `json:"crawler"`
+	IgnoreEntryUpdates          bool   `json:"ignore_entry_updates"`
 	Disabled                    bool   `json:"disabled"`
 	NoMediaPlayer               bool   `json:"no_media_player"`
 	IgnoreHTTPCache             bool   `json:"ignore_http_cache"`
@@ -195,6 +197,7 @@ type FeedModificationRequest struct {
 	BlockFilterEntryRules       *string `json:"block_filter_entry_rules"`
 	KeepFilterEntryRules        *string `json:"keep_filter_entry_rules"`
 	Crawler                     *bool   `json:"crawler"`
+	IgnoreEntryUpdates          *bool   `json:"ignore_entry_updates"`
 	UserAgent                   *string `json:"user_agent"`
 	Cookie                      *string `json:"cookie"`
 	Username                    *string `json:"username"`
@@ -260,6 +263,10 @@ func (f *FeedModificationRequest) Patch(feed *Feed) {
 		feed.Crawler = *f.Crawler
 	}
 
+	if f.IgnoreEntryUpdates != nil {
+		feed.IgnoreEntryUpdates = *f.IgnoreEntryUpdates
+	}
+
 	if f.UserAgent != nil {
 		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.Password = feedCreationRequest.Password
 	subscription.Crawler = feedCreationRequest.Crawler
+	subscription.IgnoreEntryUpdates = feedCreationRequest.IgnoreEntryUpdates
 	subscription.Disabled = feedCreationRequest.Disabled
 	subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache
 	subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates
@@ -154,6 +155,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
 	subscription.Username = feedCreationRequest.Username
 	subscription.Password = feedCreationRequest.Password
 	subscription.Crawler = feedCreationRequest.Crawler
+	subscription.IgnoreEntryUpdates = feedCreationRequest.IgnoreEntryUpdates
 	subscription.Disabled = feedCreationRequest.Disabled
 	subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache
 	subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates
@@ -338,8 +340,10 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64, forceRefresh bool
 		originalFeed.Entries = updatedFeed.Entries
 		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)
 		if storeErr != nil {
 			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,
 			disable_http2,
 			description,
-			proxy_url
+			proxy_url,
+			ignore_entry_updates
 		)
 		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
 			id
 	`
@@ -302,6 +303,7 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
 		feed.DisableHTTP2,
 		feed.Description,
 		feed.ProxyURL,
+		feed.IgnoreEntryUpdates,
 	).Scan(&feed.ID)
 	if err != nil {
 		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,
 			pushover_enabled=$36,
 			pushover_priority=$37,
-			proxy_url=$38
+			proxy_url=$38,
+			ignore_entry_updates=$39
 		WHERE
-			id=$39 AND user_id=$40
+			id=$40 AND user_id=$41
 	`
 	_, err = s.db.Exec(query,
 		feed.FeedURL,
@@ -427,6 +430,7 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
 		feed.PushoverEnabled,
 		feed.PushoverPriority,
 		feed.ProxyURL,
+		feed.IgnoreEntryUpdates,
 		feed.ID,
 		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.pushover_enabled,
 			f.pushover_priority,
-			f.proxy_url
+			f.proxy_url,
+			f.ignore_entry_updates
 		FROM
 			feeds f
 		LEFT JOIN
@@ -258,6 +259,7 @@ func (f *feedQueryBuilder) GetFeeds() (model.Feeds, error) {
 			&feed.PushoverEnabled,
 			&feed.PushoverPriority,
 			&feed.ProxyURL,
+			&feed.IgnoreEntryUpdates,
 		)
 
 		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>
             <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="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="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 }}
         <input type="hidden" name="crawler" value="1">
     {{ end }}
+    {{ if .form.IgnoreEntryUpdates }}
+        <input type="hidden" name="ignore_entry_updates" value="1">
+    {{ end }}
     {{ if .form.AllowSelfSignedCertificates }}
         <input type="hidden" name="allow_self_signed_certificates" value="1">
     {{ 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">
 
             <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="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>

+ 1 - 0
internal/ui/feed_edit.go

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

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

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

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

@@ -16,6 +16,7 @@ type SubscriptionForm struct {
 	URL                         string
 	CategoryID                  int64
 	Crawler                     bool
+	IgnoreEntryUpdates          bool
 	FetchViaProxy               bool
 	AllowSelfSignedCertificates bool
 	UserAgent                   string
@@ -73,6 +74,7 @@ func NewSubscriptionForm(r *http.Request) *SubscriptionForm {
 		URL:                         r.FormValue("url"),
 		CategoryID:                  int64(categoryID),
 		Crawler:                     r.FormValue("crawler") == "1",
+		IgnoreEntryUpdates:          r.FormValue("ignore_entry_updates") == "1",
 		AllowSelfSignedCertificates: r.FormValue("allow_self_signed_certificates") == "1",
 		FetchViaProxy:               r.FormValue("fetch_via_proxy") == "1",
 		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,
 		FeedURL:                     subscriptionForm.URL,
 		Crawler:                     subscriptionForm.Crawler,
+		IgnoreEntryUpdates:          subscriptionForm.IgnoreEntryUpdates,
 		AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 		UserAgent:                   subscriptionForm.UserAgent,
 		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,
 				AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 				Crawler:                     subscriptionForm.Crawler,
+				IgnoreEntryUpdates:          subscriptionForm.IgnoreEntryUpdates,
 				UserAgent:                   subscriptionForm.UserAgent,
 				Cookie:                      subscriptionForm.Cookie,
 				Username:                    subscriptionForm.Username,
@@ -129,6 +130,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 			CategoryID:                  subscriptionForm.CategoryID,
 			FeedURL:                     subscriptions[0].URL,
 			Crawler:                     subscriptionForm.Crawler,
+			IgnoreEntryUpdates:          subscriptionForm.IgnoreEntryUpdates,
 			AllowSelfSignedCertificates: subscriptionForm.AllowSelfSignedCertificates,
 			UserAgent:                   subscriptionForm.UserAgent,
 			Cookie:                      subscriptionForm.Cookie,